Class: RubySMB::Client
- Inherits:
-
Object
- Object
- RubySMB::Client
- Includes:
- Authentication, Echo, Negotiation, Signing, TreeConnect, Utils, Winreg
- Defined in:
- lib/ruby_smb/client.rb,
lib/ruby_smb/client/echo.rb,
lib/ruby_smb/client/utils.rb,
lib/ruby_smb/client/winreg.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, Winreg
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
- #default_domain ⇒ String
- #default_name ⇒ String
- #dialect ⇒ Integer
- #dispatcher ⇒ RubySMB::Dispatcher::Socket
- #dns_domain_name ⇒ String
- #dns_host_name ⇒ String
- #dns_tree_name ⇒ String
- #domain ⇒ String
- #local_workstation ⇒ String
- #max_buffer_size ⇒ Integer
- #ntlm_client ⇒ String
- #os_version ⇒ String
- #password ⇒ String
- #peer_native_lm ⇒ String
- #peer_native_os ⇒ String
- #primary_domain ⇒ String
- #sequence_counter ⇒ Integer
-
#server_max_buffer_size ⇒ Object
The maximum size SMB message that the Server accepts (in bytes) The default value is small by default.
- #server_max_read_size ⇒ Integer
- #server_max_transact_size ⇒ Integer
- #server_max_write_size ⇒ Integer
- #session_id ⇒ Integer
- #signing_enabled ⇒ Boolean
-
#signing_required ⇒ Object
Whether or not the Server requires signing.
- #smb1 ⇒ Boolean
- #smb2 ⇒ Boolean
- #smb2_message_id ⇒ Integer
- #user_id ⇒ String
- #username ⇒ String
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
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.
-
#net_share_enum_all(host) ⇒ Array
Returns array of shares.
-
#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_request_packet(name = '*SMBSERVER') ⇒ RubySMB::Nbss::SessionRequest
Crafts the NetBIOS SessionRequest packet to be sent for session request operations.
- #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 Winreg
#connect_to_winreg, #enum_registry_key, #enum_registry_values, #has_registry_key?, #read_registry_key_value
Methods included from Utils
#close, #create_pipe, #delete, #last_file, #last_tree, #last_tree_id, #open, #read, #tree_disconnect, #write
Methods included from Echo
Methods included from TreeConnect
#smb1_tree_connect, #smb1_tree_from_response, #smb2_tree_connect, #smb2_tree_from_response
Methods included from Signing
Methods included from Authentication
#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
Methods included from Negotiation
#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.
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 223 224 |
# File 'lib/ruby_smb/client.rb', line 184 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 # These sizes will be modifed during negotiation @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 = {} = 0 end |
Instance Attribute Details
#default_domain ⇒ String
84 85 86 |
# File 'lib/ruby_smb/client.rb', line 84 def default_domain @default_domain end |
#default_name ⇒ String
79 80 81 |
# File 'lib/ruby_smb/client.rb', line 79 def default_name @default_name end |
#dialect ⇒ Integer
109 110 111 |
# File 'lib/ruby_smb/client.rb', line 109 def dialect @dialect end |
#dispatcher ⇒ RubySMB::Dispatcher::Socket
35 36 37 |
# File 'lib/ruby_smb/client.rb', line 35 def dispatcher @dispatcher end |
#dns_domain_name ⇒ String
94 95 96 |
# File 'lib/ruby_smb/client.rb', line 94 def dns_domain_name @dns_domain_name end |
#dns_host_name ⇒ String
89 90 91 |
# File 'lib/ruby_smb/client.rb', line 89 def dns_host_name @dns_host_name end |
#dns_tree_name ⇒ String
99 100 101 |
# File 'lib/ruby_smb/client.rb', line 99 def dns_tree_name @dns_tree_name end |
#domain ⇒ String
40 41 42 |
# File 'lib/ruby_smb/client.rb', line 40 def domain @domain end |
#local_workstation ⇒ String
45 46 47 |
# File 'lib/ruby_smb/client.rb', line 45 def local_workstation @local_workstation end |
#max_buffer_size ⇒ Integer
157 158 159 |
# File 'lib/ruby_smb/client.rb', line 157 def max_buffer_size @max_buffer_size end |
#ntlm_client ⇒ String
50 51 52 |
# File 'lib/ruby_smb/client.rb', line 50 def ntlm_client @ntlm_client end |
#os_version ⇒ String
104 105 106 |
# File 'lib/ruby_smb/client.rb', line 104 def os_version @os_version end |
#password ⇒ String
55 56 57 |
# File 'lib/ruby_smb/client.rb', line 55 def password @password end |
#peer_native_lm ⇒ String
67 68 69 |
# File 'lib/ruby_smb/client.rb', line 67 def peer_native_lm @peer_native_lm end |
#peer_native_os ⇒ String
61 62 63 |
# File 'lib/ruby_smb/client.rb', line 61 def peer_native_os @peer_native_os end |
#primary_domain ⇒ String
74 75 76 |
# File 'lib/ruby_smb/client.rb', line 74 def primary_domain @primary_domain end |
#sequence_counter ⇒ Integer
116 117 118 |
# File 'lib/ruby_smb/client.rb', line 116 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
163 164 165 |
# File 'lib/ruby_smb/client.rb', line 163 def server_max_buffer_size @server_max_buffer_size end |
#server_max_read_size ⇒ Integer
173 174 175 |
# File 'lib/ruby_smb/client.rb', line 173 def server_max_read_size @server_max_read_size end |
#server_max_transact_size ⇒ Integer
179 180 181 |
# File 'lib/ruby_smb/client.rb', line 179 def server_max_transact_size @server_max_transact_size end |
#server_max_write_size ⇒ Integer
168 169 170 |
# File 'lib/ruby_smb/client.rb', line 168 def server_max_write_size @server_max_write_size end |
#session_id ⇒ Integer
121 122 123 |
# File 'lib/ruby_smb/client.rb', line 121 def session_id @session_id end |
#signing_enabled ⇒ Boolean
126 |
# File 'lib/ruby_smb/client.rb', line 126 attr_accessor :signing_required |
#signing_required ⇒ Object
Whether or not the Server requires signing
126 127 128 |
# File 'lib/ruby_smb/client.rb', line 126 def signing_required @signing_required end |
#smb1 ⇒ Boolean
131 132 133 |
# File 'lib/ruby_smb/client.rb', line 131 def smb1 @smb1 end |
#smb2 ⇒ Boolean
136 137 138 |
# File 'lib/ruby_smb/client.rb', line 136 def smb2 @smb2 end |
#smb2_message_id ⇒ Integer
141 142 143 |
# File 'lib/ruby_smb/client.rb', line 141 def end |
#user_id ⇒ String
151 152 153 |
# File 'lib/ruby_smb/client.rb', line 151 def user_id @user_id end |
#username ⇒ String
146 147 148 |
# File 'lib/ruby_smb/client.rb', line 146 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.
230 231 232 233 234 235 236 237 |
# File 'lib/ruby_smb/client.rb', line 230 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.
245 246 247 248 249 250 251 252 |
# File 'lib/ruby_smb/client.rb', line 245 def echo(count: 1, data: '') response = if smb2 smb2_echo else smb1_echo(count: count, data: data) end response.status_code end |
#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. It then increments the counter on the client.
260 261 262 263 264 265 266 |
# File 'lib/ruby_smb/client.rb', line 260 def (packet) if packet.smb2_header..zero? && != 0 packet.smb2_header. = self. += 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.
270 271 272 273 274 |
# File 'lib/ruby_smb/client.rb', line 270 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
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/ruby_smb/client.rb', line 303 def logoff! if smb2 request = RubySMB::SMB2::Packet::LogoffRequest.new raw_response = send_recv(request) response = RubySMB::SMB2::Packet::LogoffResponse.read(raw_response) unless response.valid? raise RubySMB::Error::InvalidPacket.new( expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID, expected_cmd: RubySMB::SMB2::Packet::LogoffResponse::COMMAND, received_proto: response.smb2_header.protocol, received_cmd: response.smb2_header.command ) end else request = RubySMB::SMB1::Packet::LogoffRequest.new raw_response = send_recv(request) response = RubySMB::SMB1::Packet::LogoffResponse.read(raw_response) unless response.valid? raise RubySMB::Error::InvalidPacket.new( expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID, expected_cmd: RubySMB::SMB1::Packet::LogoffResponse::COMMAND, received_proto: response.smb_header.protocol, received_cmd: response.smb_header.command ) end end wipe_state! response.status_code end |
#net_share_enum_all(host) ⇒ Array
Returns array of shares
378 379 380 381 382 |
# File 'lib/ruby_smb/client.rb', line 378 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.
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/ruby_smb/client.rb', line 338 def send_recv(packet) case packet.packet_smb_version when 'SMB1' packet.smb_header.uid = user_id if user_id packet = smb1_sign(packet) when 'SMB2' packet = (packet) packet.smb2_header.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.
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
# File 'lib/ruby_smb/client.rb', line 403 def session_request(name = '*SMBSERVER') session_request = session_request_packet(name) dispatcher.send_packet(session_request, nbss_header: false) raw_response = dispatcher.recv_packet(full_response: true) begin session_header = RubySMB::Nbss::SessionHeader.read(raw_response) if session_header.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 rescue IOError raise RubySMB::Error::InvalidPacket, 'Not a NBSS packet' end return true end |
#session_request_packet(name = '*SMBSERVER') ⇒ RubySMB::Nbss::SessionRequest
Crafts the NetBIOS SessionRequest packet to be sent for session request operations.
424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/ruby_smb/client.rb', line 424 def session_request_packet(name = '*SMBSERVER') called_name = "#{name.upcase.ljust(15)}\x20" calling_name = "#{''.ljust(15)}\x00" session_request = RubySMB::Nbss::SessionRequest.new session_request.session_header.session_packet_type = RubySMB::Nbss::SESSION_REQUEST session_request.called_name = called_name session_request.calling_name = calling_name session_request.session_header.packet_length = session_request.num_bytes - session_request.session_header.num_bytes session_request end |
#session_setup(user, pass, domain, do_recv = true, local_workstation: self.local_workstation) ⇒ Object
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/ruby_smb/client.rb', line 276 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 |
#tree_connect(share) ⇒ RubySMB::SMB1::Tree, RubySMB::SMB2::Tree
Connects to the supplied share
364 365 366 367 368 369 370 371 372 |
# File 'lib/ruby_smb/client.rb', line 364 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.
389 390 391 392 393 394 395 |
# File 'lib/ruby_smb/client.rb', line 389 def wipe_state! self.session_id = 0x00 self.user_id = 0x00 self.session_key = '' self.sequence_counter = 0 self. = 0 end |