ruby_smb 3.2.4 → 3.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/cortex.yaml +15 -0
  4. data/examples/dump_secrets_from_sid.rb +1 -1
  5. data/lib/ruby_smb/dcerpc/alter_context.rb +30 -0
  6. data/lib/ruby_smb/dcerpc/alter_context_resp.rb +42 -0
  7. data/lib/ruby_smb/dcerpc/bind.rb +3 -35
  8. data/lib/ruby_smb/dcerpc/bind_ack.rb +0 -31
  9. data/lib/ruby_smb/dcerpc/client.rb +4 -0
  10. data/lib/ruby_smb/dcerpc/drsr.rb +13 -13
  11. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_decrypt_file_srv_request.rb +22 -0
  12. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_decrypt_file_srv_response.rb +21 -0
  13. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_query_recover_agents_request.rb +20 -0
  14. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_query_recover_agents_response.rb +21 -0
  15. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_query_users_on_file_request.rb +20 -0
  16. data/lib/ruby_smb/dcerpc/encrypting_file_system/efs_rpc_query_users_on_file_response.rb +21 -0
  17. data/lib/ruby_smb/dcerpc/encrypting_file_system.rb +52 -0
  18. data/lib/ruby_smb/dcerpc/p_cont_list_t.rb +37 -0
  19. data/lib/ruby_smb/dcerpc/p_result_list_t.rb +13 -0
  20. data/lib/ruby_smb/dcerpc/p_result_t.rb +15 -0
  21. data/lib/ruby_smb/dcerpc/port_any_t.rb +11 -0
  22. data/lib/ruby_smb/dcerpc/request.rb +8 -3
  23. data/lib/ruby_smb/dcerpc/response.rb +6 -1
  24. data/lib/ruby_smb/dcerpc.rb +165 -122
  25. data/lib/ruby_smb/ntlm/custom/string_encoder.rb +22 -0
  26. data/lib/ruby_smb/ntlm.rb +1 -1
  27. data/lib/ruby_smb/version.rb +1 -1
  28. data/lib/ruby_smb.rb +1 -1
  29. data/spec/lib/ruby_smb/dcerpc/client_spec.rb +31 -16
  30. data/spec/lib/ruby_smb/dcerpc/drsr_spec.rb +4 -1
  31. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +0 -6
  32. data/spec/lib/ruby_smb/dcerpc/response_spec.rb +0 -6
  33. data/spec/lib/ruby_smb/dcerpc/sec_trailer_spec.rb +0 -14
  34. data.tar.gz.sig +0 -0
  35. metadata +16 -3
  36. metadata.gz.sig +0 -0
  37. data/lib/ruby_smb/ntlm/custom/ntlm.rb +0 -19
@@ -51,11 +51,128 @@ module RubySMB
51
51
  require 'ruby_smb/dcerpc/request'
52
52
  require 'ruby_smb/dcerpc/response'
53
53
  require 'ruby_smb/dcerpc/rpc_auth3'
54
+ require 'ruby_smb/dcerpc/port_any_t'
55
+ require 'ruby_smb/dcerpc/p_cont_list_t'
56
+ require 'ruby_smb/dcerpc/p_result_t'
57
+ require 'ruby_smb/dcerpc/p_result_list_t'
58
+ require 'ruby_smb/dcerpc/alter_context'
54
59
  require 'ruby_smb/dcerpc/bind'
55
60
  require 'ruby_smb/dcerpc/bind_ack'
61
+ require 'ruby_smb/dcerpc/alter_context_resp'
56
62
  require 'ruby_smb/dcerpc/print_system'
57
63
  require 'ruby_smb/dcerpc/encrypting_file_system'
58
64
 
65
+ # Initialize the auth provider using NTLM. This function should be overriden for other providers (e.g. Kerberos, etc.)
66
+ # @raise ArgumentError If @ntlm_client isn't initialized with a username and password.
67
+ # @return Serialized message for initializing the auth provider (NTLM, unless this class is extended/overridden)
68
+ def auth_provider_init
69
+ raise ArgumentError, "NTLM Client not initialized. Username and password must be provided" unless @ntlm_client
70
+ type1_message = @ntlm_client.init_context
71
+
72
+ type1_message.serialize
73
+ end
74
+
75
+ # Encrypt the value in dcerpc_req.stub, and add a valid signature to the request.
76
+ # This function modifies the request object in-place, and does not return anything.
77
+ # This function should be overriden for other providers (e.g. Kerberos, etc.)
78
+ # @param dcerpc_req [Request] The Request object to be encrypted and signed in-place
79
+ def auth_provider_encrypt_and_sign(dcerpc_req)
80
+ auth_type = dcerpc_req.sec_trailer.auth_type
81
+ auth_level = dcerpc_req.sec_trailer.auth_level
82
+ unless [RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT].include?(auth_type)
83
+ raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
84
+ end
85
+ plaintext = dcerpc_req.stub.to_binary_s
86
+ pad_length = get_auth_padding_length(plaintext.length)
87
+ dcerpc_req.auth_pad = "\x00" * pad_length
88
+ data_to_sign = plain_stub_with_padding = dcerpc_req.stub.to_binary_s + dcerpc_req.auth_pad.to_binary_s
89
+ dcerpc_req.sec_trailer.auth_pad_length = pad_length
90
+ if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
91
+ data_to_sign = dcerpc_req.to_binary_s[0..-(dcerpc_req.pdu_header.auth_length + 1)]
92
+ end
93
+
94
+ if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
95
+ encrypted = @ntlm_client.session.seal_message(plain_stub_with_padding)
96
+ set_encrypted_packet(dcerpc_req, encrypted, pad_length)
97
+ end
98
+ signature = @ntlm_client.session.sign_message(data_to_sign)
99
+
100
+ set_signature_on_packet(dcerpc_req, signature)
101
+ end
102
+
103
+ # Get the response's full stub value (which will include the auth-pad)
104
+ # @param dcerpc_response [Response] The Response object to extract from
105
+ # @return [String] The full stub, including auth_pad
106
+ def get_response_full_stub(dcerpc_response)
107
+ dcerpc_response.stub.to_binary_s + dcerpc_response.auth_pad.to_binary_s
108
+ end
109
+
110
+ # Decrypt the value in dcerpc_req.stub, and validate its signature.
111
+ # This function modifies the request object in-place, and returns whether the signature was valid.
112
+ # This function should be overriden for other providers (e.g. Kerberos, etc.)
113
+ # @param dcerpc_response [Response] The Response packet to decrypt and verify in-place
114
+ # @raise ArgumentError If the auth type is not NTLM
115
+ # @return [Boolean] Is the packet's signature valid?
116
+ def auth_provider_decrypt_and_verify(dcerpc_response)
117
+ auth_type = dcerpc_response.sec_trailer.auth_type
118
+ auth_level = dcerpc_response.sec_trailer.auth_level
119
+ unless [RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT].include?(auth_type)
120
+ raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
121
+ end
122
+ signature = dcerpc_response.auth_value
123
+ if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
124
+ encrypted_stub = get_response_full_stub(dcerpc_response)
125
+ plaintext = @ntlm_client.session.unseal_message(encrypted_stub)
126
+ set_decrypted_packet(dcerpc_response, plaintext)
127
+ end
128
+ data_to_check = dcerpc_response.stub.to_binary_s
129
+ if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
130
+ data_to_check = dcerpc_response.to_binary_s[0..-(dcerpc_response.pdu_header.auth_length + 1)]
131
+ end
132
+
133
+ @ntlm_client.session.verify_signature(signature, data_to_check)
134
+ end
135
+
136
+ # Completes local initialisation of @ntlm_client using the server's response
137
+ #
138
+ # @param type2_message [String] NTLM type 2 message sent from server
139
+ # @return [String] Type 3 message to be sent to the server to complete the NTLM handshake
140
+ def process_ntlm_type2(type2_message)
141
+ ntlmssp_offset = type2_message.index('NTLMSSP')
142
+ type2_blob = type2_message.slice(ntlmssp_offset..-1)
143
+ type2_b64_message = [type2_blob].pack('m')
144
+ type3_message = @ntlm_client.init_context(type2_b64_message)
145
+ auth3 = type3_message.serialize
146
+
147
+ @session_key = @ntlm_client.session_key
148
+ auth3
149
+ end
150
+
151
+ # Send a rpc_auth3 PDU that ends the authentication handshake.
152
+ # This function should be overriden for other providers (e.g. Kerberos, etc.)
153
+ #
154
+ # @param response [BindAck] the BindAck response packet
155
+ # @param options [Hash] Unused by the NTLM auth provider
156
+ def auth_provider_complete_handshake(response, options)
157
+ auth3 = process_ntlm_type2(response.auth_value)
158
+
159
+ rpc_auth3 = RpcAuth3.new
160
+ add_auth_verifier(rpc_auth3, auth3)
161
+ rpc_auth3.pdu_header.call_id = @call_id
162
+
163
+ # The server should not respond
164
+ send_packet(rpc_auth3)
165
+ @call_id += 1
166
+
167
+ nil
168
+ end
169
+
170
+ def force_set_auth_params(auth_type, auth_level)
171
+ @auth_type = auth_type
172
+ @auth_level = auth_level
173
+ end
174
+
175
+
59
176
  # Bind to the remote server interface endpoint. It takes care of adding
60
177
  # the necessary authentication verifier if `:auth_level` is set to
61
178
  # anything different than RPC_C_AUTHN_LEVEL_NONE
@@ -74,23 +191,16 @@ module RubySMB
74
191
  @call_id ||= 1
75
192
  bind_req = Bind.new(options)
76
193
  bind_req.pdu_header.call_id = @call_id
194
+ auth_type = options.fetch(:auth_type) { RPC_C_AUTHN_WINNT }
195
+ auth_level = options.fetch(:auth_level) { RPC_C_AUTHN_LEVEL_NONE }
77
196
 
78
- if options[:auth_level] && options[:auth_level] != RPC_C_AUTHN_LEVEL_NONE
79
- case options[:auth_type]
80
- when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
81
- @ctx_id = 0
82
- @auth_ctx_id_base = rand(0xFFFFFFFF)
83
- raise ArgumentError, "NTLM Client not initialized. Username and password must be provided" unless @ntlm_client
84
- type1_message = @ntlm_client.init_context
85
- auth = type1_message.serialize
86
- when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE
87
- when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL
88
- # TODO
89
- raise NotImplementedError
90
- else
91
- raise ArgumentError, "Unsupported Auth Type: #{options[:auth_type]}"
92
- end
93
- add_auth_verifier(bind_req, auth, options[:auth_type], options[:auth_level])
197
+ force_set_auth_params(auth_type, auth_level)
198
+
199
+ if @auth_level != RPC_C_AUTHN_LEVEL_NONE
200
+ @ctx_id = 0
201
+ @auth_ctx_id_base = rand(0xFFFFFFFF)
202
+ auth = auth_provider_init
203
+ add_auth_verifier(bind_req, auth)
94
204
  end
95
205
 
96
206
  send_packet(bind_req)
@@ -110,17 +220,11 @@ module RubySMB
110
220
  self.max_buffer_size = dcerpc_response.max_xmit_frag
111
221
  @call_id = dcerpc_response.pdu_header.call_id
112
222
 
113
- if options[:auth_level] && options[:auth_level] != RPC_C_AUTHN_LEVEL_NONE
223
+ if auth_level != RPC_C_AUTHN_LEVEL_NONE
114
224
  # The number of legs needed to build the security context is defined
115
225
  # by the security provider
116
- # (see [2.2.1.1.7 Security Providers](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/d4097450-c62f-484b-872f-ddf59a7a0d36))
117
- case options[:auth_type]
118
- when RPC_C_AUTHN_WINNT
119
- send_auth3(dcerpc_response, options[:auth_type], options[:auth_level])
120
- when RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE
121
- # TODO
122
- raise NotImplementedError
123
- end
226
+ # (see [2.2.1.1.7 Security Providers](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/d4097450-c62f-484b-872f-ddf59a7a0d36))
227
+ auth_provider_complete_handshake(dcerpc_response, options)
124
228
  end
125
229
 
126
230
  dcerpc_response
@@ -156,16 +260,24 @@ module RubySMB
156
260
  nil
157
261
  end
158
262
 
263
+ def get_auth_padding_length(plaintext_len)
264
+ (16 - (plaintext_len % 16)) % 16
265
+ end
266
+
159
267
  # Add the authentication verifier to a Request packet. This includes a
160
268
  # sec trailer and the signature of the packet. This also encrypts the
161
269
  # Request stub if privacy is required (`:auth_level` option is
162
270
  # RPC_C_AUTHN_LEVEL_PKT_PRIVACY).
163
271
  #
164
272
  # @param dcerpc_req [Request] the Request packet to be updated
165
- # @param opts [Hash] the authenticaiton options: `:auth_type` and `:auth_level`
273
+ # @param opts [Hash] the authentication options: `:auth_type` and `:auth_level`
166
274
  # @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
167
275
  # @raise [ArgumentError] if `:auth_type` is unknown
168
276
  def set_integrity_privacy(dcerpc_req, auth_level:, auth_type:)
277
+ unless [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(auth_level)
278
+ raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
279
+ end
280
+
169
281
  dcerpc_req.sec_trailer = {
170
282
  auth_type: auth_type,
171
283
  auth_level: auth_level,
@@ -174,35 +286,30 @@ module RubySMB
174
286
  dcerpc_req.auth_value = ' ' * 16
175
287
  dcerpc_req.pdu_header.auth_length = 16
176
288
 
177
- data_to_sign = plain_stub = dcerpc_req.stub.to_binary_s + dcerpc_req.auth_pad.to_binary_s
178
- if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
179
- data_to_sign = dcerpc_req.to_binary_s[0..-(dcerpc_req.pdu_header.auth_length + 1)]
180
- end
181
-
182
- encrypted_stub = ''
183
- if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
184
- case auth_type
185
- when RPC_C_AUTHN_NONE
186
- when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
187
- encrypted_stub = @ntlm_client.session.seal_message(plain_stub)
188
- when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
189
- # TODO
190
- raise NotImplementedError
191
- else
192
- raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
193
- end
194
- end
289
+ auth_provider_encrypt_and_sign(dcerpc_req)
290
+ end
195
291
 
196
- signature = @ntlm_client.session.sign_message(data_to_sign)
292
+ def set_signature_on_packet(dcerpc_req, signature)
293
+ dcerpc_req.auth_value = signature
294
+ dcerpc_req.pdu_header.auth_length = signature.size
295
+ end
197
296
 
297
+ def set_encrypted_packet(dcerpc_req, encrypted_stub, pad_length)
198
298
  unless encrypted_stub.empty?
199
- pad_length = dcerpc_req.sec_trailer.auth_pad_length.to_i
200
299
  dcerpc_req.enable_encrypted_stub
201
- dcerpc_req.stub = encrypted_stub[0..-(pad_length + 1)]
202
- dcerpc_req.auth_pad = encrypted_stub[-(pad_length)..-1]
300
+ dcerpc_req.stub = encrypted_stub[0..-(pad_length+1)]
301
+ if pad_length != 0
302
+ dcerpc_req.auth_pad = encrypted_stub[-(pad_length)..-1]
303
+ end
304
+ end
305
+ end
306
+
307
+ def set_decrypted_packet(dcerpc_response, decrypted_stub)
308
+ unless decrypted_stub.empty?
309
+ pad_length = dcerpc_response.sec_trailer.auth_pad_length.to_i
310
+ dcerpc_response.stub = decrypted_stub[0..-(pad_length + 1)]
311
+ dcerpc_response.auth_pad = decrypted_stub[-(pad_length + 1)..-1]
203
312
  end
204
- dcerpc_req.auth_value = signature
205
- dcerpc_req.pdu_header.auth_length = signature.size
206
313
  end
207
314
 
208
315
  # Process the security context received in a response. It decrypts the
@@ -214,39 +321,18 @@ module RubySMB
214
321
  #
215
322
  # @param dcerpc_response [Response] the Response packet
216
323
  # containing the security context to process
217
- # @param opts [Hash] the authenticaiton options: `:auth_type` and
324
+ # @param opts [Hash] the authentication options: `:auth_type` and
218
325
  # `:auth_level`. To enable errors when signature check fails, set the
219
326
  # `:raise_signature_error` option to true
220
327
  # @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
221
328
  # @raise [Error::CommunicationError] if socket-related error occurs
222
329
  def handle_integrity_privacy(dcerpc_response, auth_level:, auth_type:, raise_signature_error: false)
223
- decrypted_stub = ''
224
- if auth_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY
225
- encrypted_stub = dcerpc_response.stub.to_binary_s + dcerpc_response.auth_pad.to_binary_s
226
- case auth_type
227
- when RPC_C_AUTHN_NONE
228
- when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
229
- decrypted_stub = @ntlm_client.session.unseal_message(encrypted_stub)
230
- when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
231
- # TODO
232
- raise NotImplementedError
233
- else
234
- raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
235
- end
236
- end
237
-
238
- unless decrypted_stub.empty?
239
- pad_length = dcerpc_response.sec_trailer.auth_pad_length.to_i
240
- dcerpc_response.stub = decrypted_stub[0..-(pad_length + 1)]
241
- dcerpc_response.auth_pad = decrypted_stub[-(pad_length)..-1]
330
+ unless [RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY].include?(auth_level)
331
+ raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
242
332
  end
333
+ signature_valid = auth_provider_decrypt_and_verify(dcerpc_response)
243
334
 
244
- signature = dcerpc_response.auth_value
245
- data_to_check = dcerpc_response.stub.to_binary_s
246
- if @ntlm_client.flags & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] != 0
247
- data_to_check = dcerpc_response.to_binary_s[0..-(dcerpc_response.pdu_header.auth_length + 1)]
248
- end
249
- unless @ntlm_client.session.verify_signature(signature, data_to_check)
335
+ unless signature_valid
250
336
  if raise_signature_error
251
337
  raise Error::InvalidPacket.new(
252
338
  "Wrong packet signature received (set `raise_signature_error` to false to ignore)"
@@ -264,12 +350,10 @@ module RubySMB
264
350
  #
265
351
  # @param req [BinData::Record] the request to be updated
266
352
  # @param auth [String] the authentication data
267
- # @param auth_type [Integer] the authentication type
268
- # @param auth_level [Integer] the authentication level
269
- def add_auth_verifier(req, auth, auth_type, auth_level)
353
+ def add_auth_verifier(req, auth)
270
354
  req.sec_trailer = {
271
- auth_type: auth_type,
272
- auth_level: auth_level,
355
+ auth_type: @auth_type,
356
+ auth_level: @auth_level,
273
357
  auth_context_id: @ctx_id + @auth_ctx_id_base
274
358
  }
275
359
  req.auth_value = auth
@@ -277,46 +361,5 @@ module RubySMB
277
361
 
278
362
  nil
279
363
  end
280
-
281
- def process_ntlm_type2(type2_message)
282
- ntlmssp_offset = type2_message.index('NTLMSSP')
283
- type2_blob = type2_message.slice(ntlmssp_offset..-1)
284
- type2_b64_message = [type2_blob].pack('m')
285
- type3_message = @ntlm_client.init_context(type2_b64_message)
286
- auth3 = type3_message.serialize
287
-
288
- @session_key = @ntlm_client.session_key
289
- auth3
290
- end
291
-
292
- # Send a rpc_auth3 PDU that ends the authentication handshake.
293
- #
294
- # @param response [BindAck] the BindAck response packet
295
- # @param auth_type [Integer] the authentication type
296
- # @param auth_level [Integer] the authentication level
297
- # @raise [ArgumentError] if `:auth_type` is unknown
298
- # @raise [NotImplementedError] if `:auth_type` is not implemented (yet)
299
- def send_auth3(response, auth_type, auth_level)
300
- case auth_type
301
- when RPC_C_AUTHN_NONE
302
- when RPC_C_AUTHN_WINNT, RPC_C_AUTHN_DEFAULT
303
- auth3 = process_ntlm_type2(response.auth_value)
304
- when RPC_C_AUTHN_NETLOGON, RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_GSS_SCHANNEL, RPC_C_AUTHN_GSS_KERBEROS
305
- # TODO
306
- raise NotImplementedError
307
- else
308
- raise ArgumentError, "Unsupported Auth Type: #{auth_type}"
309
- end
310
-
311
- rpc_auth3 = RpcAuth3.new
312
- add_auth_verifier(rpc_auth3, auth3, auth_type, auth_level)
313
- rpc_auth3.pdu_header.call_id = @call_id
314
-
315
- # The server should not respond
316
- send_packet(rpc_auth3)
317
- @call_id += 1
318
-
319
- nil
320
- end
321
364
  end
322
365
  end
@@ -0,0 +1,22 @@
1
+ require 'net/ntlm'
2
+
3
+ module RubySMB
4
+ module NTLM
5
+ module Custom
6
+ module StringEncoder
7
+
8
+ def self.prepended(base)
9
+ base.singleton_class.send(:prepend, ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ def encode_utf16le(str)
14
+ str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('ASCII-8BIT')
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ Net::NTLM::EncodeUtil.send(:prepend, RubySMB::NTLM::Custom::StringEncoder)
data/lib/ruby_smb/ntlm.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'ruby_smb/ntlm/custom/ntlm'
1
+ require 'ruby_smb/ntlm/custom/string_encoder'
2
2
 
3
3
  module RubySMB
4
4
  module NTLM
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.2.4'.freeze
2
+ VERSION = '3.2.6'.freeze
3
3
  end
data/lib/ruby_smb.rb CHANGED
@@ -6,7 +6,7 @@ require 'openssl/ccm'
6
6
  require 'openssl/cmac'
7
7
  require 'windows_error'
8
8
  require 'windows_error/nt_status'
9
- require 'ruby_smb/ntlm/custom/ntlm'
9
+ require 'ruby_smb/ntlm/custom/string_encoder'
10
10
  # A packet parsing and manipulation library for the SMB1 and SMB2 protocols
11
11
  #
12
12
  # [[MS-SMB] Server Message Block (SMB) Protocol Version 1](https://msdn.microsoft.com/en-us/library/cc246482.aspx)
@@ -9,7 +9,12 @@ RSpec.describe RubySMB::Dcerpc::Client do
9
9
  let(:endpoint) { RubySMB::Dcerpc::Samr }
10
10
 
11
11
  subject(:client) { described_class.new(host, endpoint) }
12
- subject(:auth_client) { described_class.new(host, endpoint, username: 'testuser', password: '1234') }
12
+ subject(:auth_client) do
13
+ result = described_class.new(host, endpoint, username: 'testuser', password: '1234')
14
+ result.force_set_auth_params(RubySMB::Dcerpc::RPC_C_AUTHN_WINNT, RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
15
+
16
+ result
17
+ end
13
18
 
14
19
  it { is_expected.to respond_to :domain }
15
20
  it { is_expected.to respond_to :local_workstation }
@@ -135,21 +140,21 @@ RSpec.describe RubySMB::Dcerpc::Client do
135
140
  describe '#add_auth_verifier' do
136
141
  let(:req) { RubySMB::Dcerpc::Bind.new }
137
142
  let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
138
- let(:auth_level) { 0 }
143
+ let(:auth_level) { RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY }
139
144
  let(:auth) { 'serialized auth value' }
140
145
 
141
146
  it 'sets #auth_value field to the expected value' do
142
- auth_client.add_auth_verifier(req, auth, auth_type, auth_level)
147
+ auth_client.add_auth_verifier(req, auth)
143
148
  expect(req.auth_value).to eq(auth)
144
149
  end
145
150
 
146
151
  it 'sets PDUHeader #auth_length field to the expected value' do
147
- auth_client.add_auth_verifier(req, auth, auth_type, auth_level)
152
+ auth_client.add_auth_verifier(req, auth)
148
153
  expect(req.pdu_header.auth_length).to eq(auth.length)
149
154
  end
150
155
 
151
156
  it 'sets #sec_trailer field to the expected value' do
152
- auth_client.add_auth_verifier(req, auth, auth_type, auth_level)
157
+ auth_client.add_auth_verifier(req, auth)
153
158
  expect(req.sec_trailer.auth_type).to eq(auth_type)
154
159
  expect(req.sec_trailer.auth_level).to eq(auth_level)
155
160
  expect(req.sec_trailer.auth_context_id).to eq(auth_client.instance_variable_get(:@auth_ctx_id_base))
@@ -202,29 +207,29 @@ RSpec.describe RubySMB::Dcerpc::Client do
202
207
  end
203
208
 
204
209
  it 'add an auth verifier to the RpcAuth3 packet' do
205
- auth_client.send_auth3(bindack, auth_type, auth_level)
206
- expect(auth_client).to have_received(:add_auth_verifier).with(rpc_auth3, auth3, auth_type, auth_level)
210
+ auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
211
+ expect(auth_client).to have_received(:add_auth_verifier).with(rpc_auth3, auth3)
207
212
  end
208
213
 
209
214
  it 'sets the PDUHeader #call_id to the expected value' do
210
215
  auth_client.instance_variable_set(:@call_id, 56)
211
- auth_client.send_auth3(bindack, auth_type, auth_level)
216
+ auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
212
217
  expect(rpc_auth3.pdu_header.call_id).to eq(56)
213
218
  end
214
219
 
215
220
  it 'sends the RpcAuth3 packet' do
216
- auth_client.send_auth3(bindack, auth_type, auth_level)
221
+ auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
217
222
  expect(auth_client).to have_received(:send_packet).with(rpc_auth3)
218
223
  end
219
224
 
220
225
  it 'increments #call_id' do
221
- auth_client.send_auth3(bindack, auth_type, auth_level)
226
+ auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
222
227
  expect(auth_client.instance_variable_get(:@call_id)).to eq(2)
223
228
  end
224
229
 
225
230
  context 'with RPC_C_AUTHN_WINNT auth_type' do
226
231
  it 'processes NTLM type2 message' do
227
- auth_client.send_auth3(bindack, auth_type, auth_level)
232
+ auth_client.auth_provider_complete_handshake(bindack, auth_type: auth_type, auth_level: auth_level)
228
233
  expect(auth_client).to have_received(:process_ntlm_type2).with(auth)
229
234
  end
230
235
  end
@@ -300,7 +305,7 @@ RSpec.describe RubySMB::Dcerpc::Client do
300
305
 
301
306
  context 'with RPC_C_AUTHN_WINNT auth_type' do
302
307
  let(:kwargs) do {
303
- auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_DEFAULT,
308
+ auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
304
309
  auth_type: RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
305
310
  }
306
311
  end
@@ -310,7 +315,7 @@ RSpec.describe RubySMB::Dcerpc::Client do
310
315
  allow(client.ntlm_client).to receive(:init_context).and_return(type1_message)
311
316
  allow(type1_message).to receive(:serialize).and_return(auth)
312
317
  allow(client).to receive(:add_auth_verifier)
313
- allow(client).to receive(:send_auth3)
318
+ allow(client).to receive(:auth_provider_complete_handshake)
314
319
  end
315
320
 
316
321
  it 'raises an exception if the NTLM client is not initialized' do
@@ -320,12 +325,12 @@ RSpec.describe RubySMB::Dcerpc::Client do
320
325
 
321
326
  it 'adds the auth verifier with a NTLM type1 message' do
322
327
  client.bind(**kwargs)
323
- expect(client).to have_received(:add_auth_verifier).with(bind_req, auth, kwargs[:auth_type], kwargs[:auth_level])
328
+ expect(client).to have_received(:add_auth_verifier).with(bind_req, auth)
324
329
  end
325
330
 
326
331
  it 'sends an auth3 request' do
327
332
  client.bind(**kwargs)
328
- expect(client).to have_received(:send_auth3).with(bindack_response, kwargs[:auth_type], kwargs[:auth_level])
333
+ expect(client).to have_received(:auth_provider_complete_handshake).with(bindack_response, auth_type: kwargs[:auth_type], auth_level: kwargs[:auth_level])
329
334
  end
330
335
  end
331
336
  end
@@ -372,10 +377,14 @@ RSpec.describe RubySMB::Dcerpc::Client do
372
377
 
373
378
  describe '#set_integrity_privacy' do
374
379
  let(:dcerpc_req) do
375
- RubySMB::Dcerpc::Request.new(
380
+ req = RubySMB::Dcerpc::Request.new(
376
381
  { opnum: RubySMB::Dcerpc::Winreg::REG_ENUM_KEY },
377
382
  { endpoint: 'Winreg' }
378
383
  )
384
+ req.sec_trailer.auth_pad_length = 8
385
+ req.auth_pad = "\x00" * 8
386
+
387
+ req
379
388
  end
380
389
  let(:auth_level) { RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY }
381
390
  let(:auth_type) { RubySMB::Dcerpc::RPC_C_AUTHN_WINNT }
@@ -656,6 +665,8 @@ RSpec.describe RubySMB::Dcerpc::Client do
656
665
  # Make sure the encrypted stub includes the correct pad to make sure sec_trailer is 16-bytes aligned
657
666
  allow(session).to receive(:unseal_message).and_return(decrypted_stub + auth_pad)
658
667
  allow(session).to receive(:verify_signature).and_return true
668
+ dcerpc_res.sec_trailer.auth_type = auth_type
669
+ dcerpc_res.sec_trailer.auth_level = auth_level
659
670
  end
660
671
 
661
672
  it 'verifies the signature' do
@@ -698,6 +709,8 @@ RSpec.describe RubySMB::Dcerpc::Client do
698
709
  context 'without RPC_C_AUTHN_LEVEL_PKT_PRIVACY auth_level' do
699
710
  it 'does not encrypt the stub' do
700
711
  plain_stub = dcerpc_res.stub.to_binary_s
712
+ dcerpc_res.sec_trailer.auth_level = RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
713
+ dcerpc_res.sec_trailer.auth_type = RubySMB::Dcerpc::RPC_C_AUTHN_WINNT
701
714
  auth_client.handle_integrity_privacy(dcerpc_res, auth_level: RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, auth_type: auth_type)
702
715
  expect(dcerpc_res.stub).to eq(plain_stub)
703
716
  expect(session).to_not have_received(:unseal_message)
@@ -706,6 +719,8 @@ RSpec.describe RubySMB::Dcerpc::Client do
706
719
 
707
720
  context 'with an unsupported auth_level' do
708
721
  it 'raises an Argument exception' do
722
+ dcerpc_res.sec_trailer.auth_level = RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_INTEGRITY
723
+ dcerpc_res.sec_trailer.auth_type = 88
709
724
  expect { auth_client.handle_integrity_privacy(dcerpc_res, auth_level: auth_level, auth_type: 88) }.to raise_error(ArgumentError)
710
725
  end
711
726
  end
@@ -6,7 +6,10 @@ RSpec.describe RubySMB::Dcerpc::Drsr do
6
6
  end
7
7
 
8
8
  let(:drsr) do
9
- RubySMB::Dcerpc::Client.new('1.2.3.4', RubySMB::Dcerpc::Drsr)
9
+ drsr = RubySMB::Dcerpc::Client.new('1.2.3.4', RubySMB::Dcerpc::Drsr)
10
+ drsr.force_set_auth_params(RubySMB::Dcerpc::RPC_C_AUTHN_WINNT, RubySMB::Dcerpc::RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
11
+
12
+ drsr
10
13
  end
11
14
 
12
15
  describe described_class::DrsHandle do
@@ -134,12 +134,6 @@ RSpec.describe RubySMB::Dcerpc::Request do
134
134
  packet.pdu_header.auth_length = 10
135
135
  expect(packet.auth_pad?).to be true
136
136
  end
137
-
138
- it 'makes sure #sec_trailer is 16-bytes aligned with the begining of the PDU body (stub)' do
139
- packet.pdu_header.auth_length = 6
140
- packet.stub = 'A' * rand(0xFF)
141
- expect((packet.sec_trailer.abs_offset - packet.stub.abs_offset) % 16).to eq(0)
142
- end
143
137
  end
144
138
 
145
139
  describe '#sec_trailer' do
@@ -77,12 +77,6 @@ RSpec.describe RubySMB::Dcerpc::Response do
77
77
  packet.pdu_header.auth_length = 10
78
78
  expect(packet.auth_pad?).to be true
79
79
  end
80
-
81
- it 'makes sure #sec_trailer is 16-bytes aligned with the begining of the PDU body (stub)' do
82
- packet.pdu_header.auth_length = 6
83
- packet.stub = 'A' * rand(0xFF)
84
- expect((packet.sec_trailer.abs_offset - packet.stub.abs_offset) % 16).to eq(0)
85
- end
86
80
  end
87
81
 
88
82
  describe '#sec_trailer' do
@@ -36,20 +36,6 @@ RSpec.describe RubySMB::Dcerpc::SecTrailer do
36
36
  expect(packet.auth_pad_length).to eq(0)
37
37
  end
38
38
 
39
- context 'when the parent structure has an #auth_pad field' do
40
- let(:pad) { 'A' * rand(0xFF) }
41
- let(:packet_with_parent) do
42
- Class.new(BinData::Record) do
43
- string :auth_pad
44
- sec_trailer :sec_trailer
45
- end.new(auth_pad: pad)
46
- end
47
-
48
- it 'has a value equal to the size of the #auth_pad field' do
49
- expect(packet_with_parent.sec_trailer.auth_pad_length).to eq(pad.size)
50
- end
51
- end
52
-
53
39
  context 'when the parent structure does not have an #auth_pad field' do
54
40
  let(:pad) { 'A' * rand(0xFF) }
55
41
  let(:packet_with_parent) do
data.tar.gz.sig CHANGED
Binary file