Class: RubySMB::Client
- Inherits:
-
Object
show all
- Includes:
- Authentication, Echo, Negotiation, Signing, TreeConnect, Utils
- Defined in:
- lib/ruby_smb/client.rb,
lib/ruby_smb/client/echo.rb,
lib/ruby_smb/client/utils.rb,
lib/ruby_smb/client/signing.rb,
lib/ruby_smb/client/negotiation.rb,
lib/ruby_smb/client/tree_connect.rb,
lib/ruby_smb/client/authentication.rb
Overview
This module holds all of the methods backing the #open_file method
Defined Under Namespace
Modules: Authentication, Echo, Negotiation, Signing, TreeConnect, Utils
Constant Summary
collapse
- SMB1_DIALECT_SMB1_DEFAULT =
The Default SMB1 Dialect string used in an SMB1 Negotiate Request
'NT LM 0.12'.freeze
- SMB1_DIALECT_SMB2_DEFAULT =
The Default SMB2 Dialect string used in an SMB1 Negotiate Request
'SMB 2.002'.freeze
- SMB2_DIALECT_DEFAULT =
Dialect value for SMB2 Default (Version 2.02)
0x0202
- MAX_BUFFER_SIZE =
The default maximum size of a SMB message that the Client accepts (in bytes)
64512
- SERVER_MAX_BUFFER_SIZE =
The default maximum size of a SMB message that the Server accepts (in bytes)
4356
Instance Attribute Summary collapse
Attributes included from Utils
#auth_user, #evasion_opts, #last_file_id, #native_lm, #native_os, #open_files, #send_lm, #send_ntlm, #spnopt, #tree_connects, #use_lanman_key, #use_ntlmv2, #usentlm2_session, #verify_signature
Attributes included from Signing
#session_key
Instance Method Summary
collapse
-
#disconnect! ⇒ void
Logs off any currently open session on the server and closes the TCP socket connection.
-
#echo(count: 1, data: '') ⇒ WindowsError::ErrorCode
Sends an Echo request to the server and returns the NTStatus of the last response packet received.
-
#increment_smb_message_id(packet) ⇒ RubySMB::GenericPacket
Sets the message id field in an SMB2 packet's header to the one tracked by the client.
-
#initialize(dispatcher, smb1: true, smb2: true, username:, password:, domain: '.', local_workstation: 'WORKSTATION') ⇒ Client
constructor
A new instance of Client.
-
#login(username: self.username, password: self.password, domain: self.domain, local_workstation: self.local_workstation) ⇒ Object
Performs protocol negotiation and session setup.
-
#logoff! ⇒ WindowsError::ErrorCode
Sends a LOGOFF command to the remote server to terminate the session.
-
#nb_name_encode(name) ⇒ Object
-
#net_share_enum_all(host) ⇒ Array
-
#send_recv(packet) ⇒ String
Sends a packet and receives the raw response through the Dispatcher.
-
#session_request(name = '*SMBSERVER') ⇒ TrueClass
Requests a NetBIOS Session Service using the provided name.
-
#session_setup(user, pass, domain, do_recv = true, local_workstation: self.local_workstation) ⇒ Object
-
#tree_connect(share) ⇒ RubySMB::SMB1::Tree, RubySMB::SMB2::Tree
Connects to the supplied share.
-
#wipe_state! ⇒ void
Resets all of the session state on the client, setting it back to scratch.
Methods included from Utils
#close, #create_pipe, #last_file, #last_tree, #last_tree_id, #open, #read, #tree_disconnect, #write
Methods included from Echo
#smb1_echo, #smb2_echo
#smb1_tree_connect, #smb1_tree_from_response, #smb2_tree_connect, #smb2_tree_from_response
Methods included from Signing
#smb1_sign, #smb2_sign
#authenticate, #extract_os_version, #smb1_anonymous_auth, #smb1_anonymous_auth_request, #smb1_anonymous_auth_response, #smb1_authenticate, #smb1_ntlmssp_auth_packet, #smb1_ntlmssp_authenticate, #smb1_ntlmssp_challenge_packet, #smb1_ntlmssp_final_packet, #smb1_ntlmssp_negotiate, #smb1_ntlmssp_negotiate_packet, #smb1_type2_message, #smb2_authenticate, #smb2_ntlmssp_auth_packet, #smb2_ntlmssp_authenticate, #smb2_ntlmssp_challenge_packet, #smb2_ntlmssp_final_packet, #smb2_ntlmssp_negotiate, #smb2_ntlmssp_negotiate_packet, #smb2_type2_message, #store_target_info
#negotiate, #negotiate_request, #negotiate_response, #parse_negotiate_response, #smb1_negotiate_request, #smb2_negotiate_request
Constructor Details
#initialize(dispatcher, smb1: true, smb2: true, username:, password:, domain: '.', local_workstation: 'WORKSTATION') ⇒ Client
Returns a new instance of Client.
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
|
# File 'lib/ruby_smb/client.rb', line 182
def initialize(dispatcher, smb1: true, smb2: true, username:, password:, domain: '.', local_workstation: 'WORKSTATION')
raise ArgumentError, 'No Dispatcher provided' unless dispatcher.is_a? RubySMB::Dispatcher::Base
if smb1 == false && smb2 == false
raise ArgumentError, 'You must enable at least one Protocol'
end
@dispatcher = dispatcher
@domain = domain
@local_workstation = local_workstation
@password = password.encode('utf-8') || ''.encode('utf-8')
@sequence_counter = 0
@session_id = 0x00
@session_key = ''
@signing_required = false
@smb1 = smb1
@smb2 = smb2
@username = username.encode('utf-8') || ''.encode('utf-8')
@max_buffer_size = MAX_BUFFER_SIZE
@server_max_buffer_size = SERVER_MAX_BUFFER_SIZE
@server_max_read_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
@server_max_write_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
@server_max_transact_size = RubySMB::SMB2::File::MAX_PACKET_SIZE
negotiate_version_flag = 0x02000000
flags = Net::NTLM::Client::DEFAULT_FLAGS |
Net::NTLM::FLAGS[:TARGET_INFO] |
negotiate_version_flag
@ntlm_client = Net::NTLM::Client.new(
@username,
@password,
workstation: @local_workstation,
domain: @domain,
flags: flags
)
@tree_connects = []
@open_files = {}
@smb2_message_id = 0
end
|
Instance Attribute Details
#default_domain ⇒ String
82
83
84
|
# File 'lib/ruby_smb/client.rb', line 82
def default_domain
@default_domain
end
|
#default_name ⇒ String
77
78
79
|
# File 'lib/ruby_smb/client.rb', line 77
def default_name
@default_name
end
|
#dialect ⇒ Integer
107
108
109
|
# File 'lib/ruby_smb/client.rb', line 107
def dialect
@dialect
end
|
33
34
35
|
# File 'lib/ruby_smb/client.rb', line 33
def dispatcher
@dispatcher
end
|
#dns_domain_name ⇒ String
92
93
94
|
# File 'lib/ruby_smb/client.rb', line 92
def dns_domain_name
@dns_domain_name
end
|
#dns_host_name ⇒ String
87
88
89
|
# File 'lib/ruby_smb/client.rb', line 87
def dns_host_name
@dns_host_name
end
|
#dns_tree_name ⇒ String
97
98
99
|
# File 'lib/ruby_smb/client.rb', line 97
def dns_tree_name
@dns_tree_name
end
|
#domain ⇒ String
38
39
40
|
# File 'lib/ruby_smb/client.rb', line 38
def domain
@domain
end
|
#local_workstation ⇒ String
43
44
45
|
# File 'lib/ruby_smb/client.rb', line 43
def local_workstation
@local_workstation
end
|
#max_buffer_size ⇒ Integer
155
156
157
|
# File 'lib/ruby_smb/client.rb', line 155
def max_buffer_size
@max_buffer_size
end
|
#ntlm_client ⇒ String
48
49
50
|
# File 'lib/ruby_smb/client.rb', line 48
def ntlm_client
@ntlm_client
end
|
#os_version ⇒ String
102
103
104
|
# File 'lib/ruby_smb/client.rb', line 102
def os_version
@os_version
end
|
#password ⇒ String
53
54
55
|
# File 'lib/ruby_smb/client.rb', line 53
def password
@password
end
|
#peer_native_lm ⇒ String
65
66
67
|
# File 'lib/ruby_smb/client.rb', line 65
def peer_native_lm
@peer_native_lm
end
|
#peer_native_os ⇒ String
59
60
61
|
# File 'lib/ruby_smb/client.rb', line 59
def peer_native_os
@peer_native_os
end
|
#primary_domain ⇒ String
72
73
74
|
# File 'lib/ruby_smb/client.rb', line 72
def primary_domain
@primary_domain
end
|
#sequence_counter ⇒ Integer
114
115
116
|
# File 'lib/ruby_smb/client.rb', line 114
def sequence_counter
@sequence_counter
end
|
#server_max_buffer_size ⇒ Object
The maximum size SMB message that the Server accepts (in bytes)
The default value is small by default
161
162
163
|
# File 'lib/ruby_smb/client.rb', line 161
def server_max_buffer_size
@server_max_buffer_size
end
|
#server_max_read_size ⇒ Integer
171
172
173
|
# File 'lib/ruby_smb/client.rb', line 171
def server_max_read_size
@server_max_read_size
end
|
#server_max_transact_size ⇒ Integer
177
178
179
|
# File 'lib/ruby_smb/client.rb', line 177
def server_max_transact_size
@server_max_transact_size
end
|
#server_max_write_size ⇒ Integer
166
167
168
|
# File 'lib/ruby_smb/client.rb', line 166
def server_max_write_size
@server_max_write_size
end
|
#session_id ⇒ Integer
119
120
121
|
# File 'lib/ruby_smb/client.rb', line 119
def session_id
@session_id
end
|
#signing_enabled ⇒ Boolean
124
|
# File 'lib/ruby_smb/client.rb', line 124
attr_accessor :signing_required
|
#signing_required ⇒ Object
Whether or not the Server requires signing
124
125
126
|
# File 'lib/ruby_smb/client.rb', line 124
def signing_required
@signing_required
end
|
#smb1 ⇒ Boolean
129
130
131
|
# File 'lib/ruby_smb/client.rb', line 129
def smb1
@smb1
end
|
#smb2 ⇒ Boolean
134
135
136
|
# File 'lib/ruby_smb/client.rb', line 134
def smb2
@smb2
end
|
#smb2_message_id ⇒ Integer
139
140
141
|
# File 'lib/ruby_smb/client.rb', line 139
def smb2_message_id
@smb2_message_id
end
|
#user_id ⇒ String
149
150
151
|
# File 'lib/ruby_smb/client.rb', line 149
def user_id
@user_id
end
|
#username ⇒ String
144
145
146
|
# File 'lib/ruby_smb/client.rb', line 144
def username
@username
end
|
Instance Method Details
#disconnect! ⇒ void
This method returns an undefined value.
Logs off any currently open session on the server
and closes the TCP socket connection.
228
229
230
231
232
233
234
235
|
# File 'lib/ruby_smb/client.rb', line 228
def disconnect!
begin
logoff!
rescue
wipe_state!
end
dispatcher.tcp_socket.close
end
|
#echo(count: 1, data: '') ⇒ WindowsError::ErrorCode
Sends an Echo request to the server and returns the
NTStatus of the last response packet received.
243
244
245
246
247
248
249
250
|
# File 'lib/ruby_smb/client.rb', line 243
def echo(count: 1, data: '')
response = if smb2
smb2_echo
else
smb1_echo(count: count, data: data)
end
response.status_code
end
|
Sets the message id field in an SMB2 packet's
header to the one tracked by the client. It then increments
the counter on the client.
258
259
260
261
262
263
264
|
# File 'lib/ruby_smb/client.rb', line 258
def increment_smb_message_id(packet)
if packet..message_id.zero? && smb2_message_id != 0
packet..message_id = smb2_message_id
self.smb2_message_id += 1
end
packet
end
|
#login(username: self.username, password: self.password, domain: self.domain, local_workstation: self.local_workstation) ⇒ Object
Performs protocol negotiation and session setup. It defaults to using
the credentials supplied during initialization, but can take a new set of credentials if needed.
268
269
270
271
272
|
# File 'lib/ruby_smb/client.rb', line 268
def login(username: self.username, password: self.password, domain: self.domain, local_workstation: self.local_workstation)
negotiate
session_setup(username, password, domain, true,
local_workstation: local_workstation)
end
|
#logoff! ⇒ WindowsError::ErrorCode
Sends a LOGOFF command to the remote server to terminate the session
#nb_name_encode(name) ⇒ Object
405
406
407
408
409
410
411
412
413
414
|
# File 'lib/ruby_smb/client.rb', line 405
def nb_name_encode(name)
encoded_name = ''
name.each_byte do |char|
first_half = (char >> 4) + 'A'.ord
second_half = (char & 0xF) + 'A'.ord
encoded_name << first_half.chr
encoded_name << second_half.chr
end
encoded_name
end
|
#net_share_enum_all(host) ⇒ Array
359
360
361
362
363
|
# File 'lib/ruby_smb/client.rb', line 359
def net_share_enum_all(host)
tree = tree_connect("\\\\#{host}\\IPC$")
named_pipe = tree.open_file(filename: "srvsvc", write: true, read: true)
named_pipe.net_share_enum_all(host)
end
|
#send_recv(packet) ⇒ String
Sends a packet and receives the raw response through the Dispatcher.
It will also sign the packet if neccessary.
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
|
# File 'lib/ruby_smb/client.rb', line 319
def send_recv(packet)
case packet.packet_smb_version
when 'SMB1'
packet..uid = user_id if user_id
packet = smb1_sign(packet)
when 'SMB2'
packet = increment_smb_message_id(packet)
packet..session_id = session_id
unless packet.is_a?(RubySMB::SMB2::Packet::SessionSetupRequest)
packet = smb2_sign(packet)
end
else
packet = packet
end
dispatcher.send_packet(packet)
raw_response = dispatcher.recv_packet
self.sequence_counter += 1 if signing_required && !session_key.empty?
raw_response
end
|
#session_request(name = '*SMBSERVER') ⇒ TrueClass
Requests a NetBIOS Session Service using the provided name.
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
|
# File 'lib/ruby_smb/client.rb', line 383
def session_request(name = '*SMBSERVER')
encoded_called_name = nb_name_encode("#{name.upcase.ljust(15)}\x20")
encoded_calling_name = nb_name_encode("#{''.ljust(15)}\x00")
session_request = RubySMB::Nbss::SessionRequest.new
session_request..session_packet_type = RubySMB::Nbss::SESSION_REQUEST
session_request.called_name = "\x20#{encoded_called_name}\x00"
session_request.calling_name = "\x20#{encoded_calling_name}\x00"
session_request..packet_length =
session_request.num_bytes - session_request..num_bytes
dispatcher.send_packet(session_request, nbss_header: false)
raw_response = dispatcher.recv_packet(full_response: true)
= RubySMB::Nbss::SessionHeader.read(raw_response)
if .session_packet_type == RubySMB::Nbss::NEGATIVE_SESSION_RESPONSE
negative_session_response = RubySMB::Nbss::NegativeSessionResponse.read(raw_response)
raise RubySMB::Error::NetBiosSessionService, "Session Request failed: #{negative_session_response.error_msg}"
end
return true
end
|
#session_setup(user, pass, domain, do_recv = true, local_workstation: self.local_workstation) ⇒ Object
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
|
# File 'lib/ruby_smb/client.rb', line 274
def session_setup(user, pass, domain, do_recv=true,
local_workstation: self.local_workstation)
@domain = domain
@local_workstation = local_workstation
@password = pass.encode('utf-8') || ''.encode('utf-8')
@username = user.encode('utf-8') || ''.encode('utf-8')
negotiate_version_flag = 0x02000000
flags = Net::NTLM::Client::DEFAULT_FLAGS |
Net::NTLM::FLAGS[:TARGET_INFO] |
negotiate_version_flag
@ntlm_client = Net::NTLM::Client.new(
@username,
@password,
workstation: @local_workstation,
domain: @domain,
flags: flags
)
authenticate
end
|
Connects to the supplied share
345
346
347
348
349
350
351
352
353
|
# File 'lib/ruby_smb/client.rb', line 345
def tree_connect(share)
connected_tree = if smb2
smb2_tree_connect(share)
else
smb1_tree_connect(share)
end
@tree_connects << connected_tree
connected_tree
end
|
#wipe_state! ⇒ void
This method returns an undefined value.
Resets all of the session state on the client, setting it
back to scratch. Should only be called when a session is no longer
valid.
370
371
372
373
374
375
376
|
# File 'lib/ruby_smb/client.rb', line 370
def wipe_state!
self.session_id = 0x00
self.user_id = 0x00
self.session_key = ''
self.sequence_counter = 0
self.smb2_message_id = 0
end
|