Module: RubySMB::Client::Authentication

Included in:
RubySMB::Client
Defined in:
lib/ruby_smb/client/authentication.rb

Overview

This module holds all the backend client methods for authentication.

Instance Method Summary collapse

Instance Method Details

#authenticateWindowsError::NTStatus

Responsible for handling Authentication and Session Setup for the SMB Client. It returns the final Status code from the authentication exchange.

Returns:

  • (WindowsError::NTStatus)

    the NTStatus object from the SessionSetup exchange.



11
12
13
14
15
16
17
# File 'lib/ruby_smb/client/authentication.rb', line 11

def authenticate
  if self.smb1
    smb1_authenticate
  else
    smb2_authenticate
  end
end

#smb1_authenticateObject

Handles the SMB1 NTLMSSP 4-way handshake for Authentication



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/ruby_smb/client/authentication.rb', line 24

def smb1_authenticate
  response = smb1_ntlmssp_negotiate
  challenge_packet = smb1_ntlmssp_challenge_packet(response)
  user_id = challenge_packet.smb_header.uid
  challenge_message = smb1_type2_message(challenge_packet)
  raw = smb1_ntlmssp_authenticate(challenge_message, user_id)
  response = smb1_ntlmssp_final_packet(raw)
  response_code = response.status_code
  self.user_id = user_id if response_code.name == "STATUS_SUCCESS"
  response_code
end

#smb1_ntlmssp_auth_packet(type2_string, user_id) ⇒ RubySMB::SMB1::Packet::SessionSetupRequest

Generates the SMB1::Packet::SessionSetupRequest packet with the NTLM Type 3 (Auth) message in the security_blob field.

Parameters:

  • type2_string (String)

    the Base64 encoded Type2 challenge to respond to

  • user_id (Integer)

    the temporary user ID from the Type 2 response

Returns:



63
64
65
66
67
68
69
70
71
72
73
# File 'lib/ruby_smb/client/authentication.rb', line 63

def smb1_ntlmssp_auth_packet(type2_string,user_id)
  type3_message = ntlm_client.init_context(type2_string)
  self.session_key = ntlm_client.session_key
  packet = RubySMB::SMB1::Packet::SessionSetupRequest.new
  packet.smb_header.uid = user_id
  packet.set_type3_blob(type3_message.serialize)
  packet.parameter_block.max_buffer_size = 4356
  packet.parameter_block.max_mpx_count = 50
  packet.smb_header.flags2.extended_security = 1
  packet
end

#smb1_ntlmssp_authenticate(type2_string, user_id) ⇒ String

Takes the Base64 encoded NTLM Type 2 (Challenge) message and calls the routines to build the Auth packet, sends the packet and receives the raw response

Parameters:

  • type2_string (String)

    the Base64 Encoded NTLM Type 2 message

  • user_id (Integer)

    the temporary user ID from the Type 2 response

Returns:

  • (String)

    the raw binary response from the server



52
53
54
55
# File 'lib/ruby_smb/client/authentication.rb', line 52

def smb1_ntlmssp_authenticate(type2_string,user_id)
  packet = smb1_ntlmssp_auth_packet(type2_string,user_id)
  send_recv(packet)
end

#smb1_ntlmssp_challenge_packet(raw_response) ⇒ Object

Takes the raw binary string and returns a SMB1::Packet::SessionSetupResponse



105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/ruby_smb/client/authentication.rb', line 105

def smb1_ntlmssp_challenge_packet(raw_response)
  packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
  status_code = packet.status_code

  unless status_code.name == "STATUS_MORE_PROCESSING_REQUIRED"
    raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
  end

  unless packet.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
    raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb_header.command} and not #{RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP}"
  end
  packet
end

#smb1_ntlmssp_final_packet(raw_response) ⇒ Object

Takes the raw binary string and returns a SMB1::Packet::SessionSetupResponse



91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/ruby_smb/client/authentication.rb', line 91

def smb1_ntlmssp_final_packet(raw_response)
  begin
    packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
  rescue
    packet = RubySMB::SMB1::Packet::EmptyPacket.read(raw_response)
  end

  unless packet.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
    raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb_header.command} and not #{RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP}"
  end
  packet
end

#smb1_ntlmssp_negotiateString

Sends the SMB1::Packet::SessionSetupRequest packet and receives the response.

Returns:

  • (String)

    the binary string response from the server



40
41
42
43
# File 'lib/ruby_smb/client/authentication.rb', line 40

def smb1_ntlmssp_negotiate
  packet = smb1_ntlmssp_negotiate_packet
  send_recv(packet)
end

#smb1_ntlmssp_negotiate_packetRubySMB::SMB1::Packet::SessionSetupRequest

Creates the SMB1::Packet::SessionSetupRequest packet for the first part of the NTLMSSP 4-way hnadshake. This packet initializes negotiations for the NTLMSSP authentication

Returns:



80
81
82
83
84
85
86
87
88
# File 'lib/ruby_smb/client/authentication.rb', line 80

def smb1_ntlmssp_negotiate_packet
  type1_message = ntlm_client.init_context
  packet = RubySMB::SMB1::Packet::SessionSetupRequest.new
  packet.set_type1_blob(type1_message.serialize)
  packet.parameter_block.max_buffer_size = 4356
  packet.parameter_block.max_mpx_count = 50
  packet.smb_header.flags2.extended_security = 1
  packet
end

#smb1_type2_message(response_packet) ⇒ String

Parses out the NTLM Type 2 Message from a SMB1::Packet::SessionSetupResponse

Parameters:

Returns:

  • (String)

    the base64 encoded NTLM Challenge (Type2 Message) from the response



123
124
125
126
127
128
# File 'lib/ruby_smb/client/authentication.rb', line 123

def smb1_type2_message(response_packet)
  sec_blob = response_packet.data_block.security_blob
  ntlmssp_offset = sec_blob.index("NTLMSSP")
  type2_blob = sec_blob.slice(ntlmssp_offset..-1)
  [type2_blob].pack("m")
end

#smb2_authenticateObject

Handles the SMB1 NTLMSSP 4-way handshake for Authentication



135
136
137
138
139
140
141
142
143
144
# File 'lib/ruby_smb/client/authentication.rb', line 135

def smb2_authenticate
  response = smb2_ntlmssp_negotiate
  challenge_packet = smb2_ntlmssp_challenge_packet(response)
  session_id = challenge_packet.smb2_header.session_id
  self.session_id = session_id
  challenge_message = smb2_type2_message(challenge_packet)
  raw = smb2_ntlmssp_authenticate(challenge_message, session_id)
  response = smb2_ntlmssp_final_packet(raw)
  response.status_code
end

#smb2_ntlmssp_auth_packet(type2_string, session_id) ⇒ RubySMB::SMB2::Packet::SessionSetupRequest

Generates the SMB2::Packet::SessionSetupRequest packet with the NTLM Type 3 (Auth) message in the security_blob field.

Parameters:

  • type2_string (String)

    the Base64 encoded Type2 challenge to respond to

  • session_id (Integer)

    the temporary session id from the Type 2 response

Returns:



223
224
225
226
227
228
229
230
# File 'lib/ruby_smb/client/authentication.rb', line 223

def smb2_ntlmssp_auth_packet(type2_string, session_id)
  type3_message = ntlm_client.init_context(type2_string)
  self.session_key = ntlm_client.session_key
  packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
  packet.smb2_header.session_id = session_id
  packet.set_type3_blob(type3_message.serialize)
  packet
end

#smb2_ntlmssp_authenticate(type2_string, user_id) ⇒ String

Takes the Base64 encoded NTLM Type 2 (Challenge) message and calls the routines to build the Auth packet, sends the packet and receives the raw response

Parameters:

  • type2_string (String)

    the Base64 Encoded NTLM Type 2 message

  • user_id (Integer)

    the temporary user ID from the Type 2 response

Returns:

  • (String)

    the raw binary response from the server



212
213
214
215
# File 'lib/ruby_smb/client/authentication.rb', line 212

def smb2_ntlmssp_authenticate(type2_string,user_id)
  packet = smb2_ntlmssp_auth_packet(type2_string,user_id)
  send_recv(packet)
end

#smb2_ntlmssp_challenge_packet(raw_response) ⇒ Object

Takes the raw binary string and returns a SMB2::Packet::SessionSetupResponse



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/ruby_smb/client/authentication.rb', line 156

def smb2_ntlmssp_challenge_packet(raw_response)
  packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
  status_code = packet.status_code
  unless status_code.name == "STATUS_MORE_PROCESSING_REQUIRED"
    raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
  end

  unless packet.smb2_header.command == RubySMB::SMB2::Commands::SESSION_SETUP
    raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb2_header.command} and not #{RubySMB::SMB2::Commands::SESSION_SETUP}"
  end
  packet
end

#smb2_ntlmssp_final_packet(raw_response) ⇒ Object

Takes the raw binary string and returns a SMB2::Packet::SessionSetupResponse



147
148
149
150
151
152
153
# File 'lib/ruby_smb/client/authentication.rb', line 147

def smb2_ntlmssp_final_packet(raw_response)
  packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
  unless packet.smb2_header.command == RubySMB::SMB2::Commands::SESSION_SETUP
    raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb2_header.command} and not #{RubySMB::SMB2::Commands::SESSION_SETUP}"
  end
  packet
end

#smb2_ntlmssp_negotiateString

Sends the SMB2::Packet::SessionSetupRequest packet and receives the response.

Returns:

  • (String)

    the binary string response from the server



173
174
175
176
# File 'lib/ruby_smb/client/authentication.rb', line 173

def smb2_ntlmssp_negotiate
  packet = smb2_ntlmssp_negotiate_packet
  send_recv(packet)
end

#smb2_ntlmssp_negotiate_packetRubySMB::SMB2::Packet::SessionSetupRequest

Creates the SMB2::Packet::SessionSetupRequest packet for the first part of the NTLMSSP 4-way handshake. This packet initializes negotiations for the NTLMSSP authentication

Returns:



183
184
185
186
187
188
189
190
191
192
# File 'lib/ruby_smb/client/authentication.rb', line 183

def smb2_ntlmssp_negotiate_packet
  type1_message = ntlm_client.init_context
  packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
  packet.set_type1_blob(type1_message.serialize)
  # This Message ID should always be 1, but thanks to Multi-Protocol Negotiation
  # the Message ID can be out of sync at this point so we re-synch it here.
  packet.smb2_header.message_id = 1
  self.smb2_message_id = 2
  packet
end

#smb2_type2_message(response_packet) ⇒ String

Parses out the NTLM Type 2 Message from a SMB2::Packet::SessionSetupResponse

Parameters:

Returns:

  • (String)

    the base64 encoded NTLM Challenge (Type2 Message) from the response



198
199
200
201
202
203
# File 'lib/ruby_smb/client/authentication.rb', line 198

def smb2_type2_message(response_packet)
  sec_blob = response_packet.buffer
  ntlmssp_offset = sec_blob.index("NTLMSSP")
  type2_blob = sec_blob.slice(ntlmssp_offset..-1)
  [type2_blob].pack("m")
end