ruby_smb 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +3 -0
  5. data/.simplecov +42 -0
  6. data/.travis.yml +5 -0
  7. data/.yardopts +1 -0
  8. data/CONTRIBUTING.md +119 -0
  9. data/Gemfile +13 -0
  10. data/LICENSE.txt +18 -0
  11. data/README.md +64 -0
  12. data/Rakefile +22 -0
  13. data/examples/authenticate.rb +30 -0
  14. data/examples/negotiate.rb +25 -0
  15. data/lib/ruby_smb/client/authentication.rb +236 -0
  16. data/lib/ruby_smb/client/negotiation.rb +126 -0
  17. data/lib/ruby_smb/client/signing.rb +48 -0
  18. data/lib/ruby_smb/client.rb +164 -0
  19. data/lib/ruby_smb/dispatcher/base.rb +18 -0
  20. data/lib/ruby_smb/dispatcher/socket.rb +53 -0
  21. data/lib/ruby_smb/dispatcher.rb +4 -0
  22. data/lib/ruby_smb/error.rb +17 -0
  23. data/lib/ruby_smb/field/file_time.rb +62 -0
  24. data/lib/ruby_smb/field/nt_status.rb +16 -0
  25. data/lib/ruby_smb/field/stringz16.rb +55 -0
  26. data/lib/ruby_smb/field.rb +7 -0
  27. data/lib/ruby_smb/generic_packet.rb +179 -0
  28. data/lib/ruby_smb/gss.rb +109 -0
  29. data/lib/ruby_smb/smb1/andx_block.rb +13 -0
  30. data/lib/ruby_smb/smb1/bit_field/capabilities.rb +39 -0
  31. data/lib/ruby_smb/smb1/bit_field/header_flags.rb +19 -0
  32. data/lib/ruby_smb/smb1/bit_field/header_flags2.rb +27 -0
  33. data/lib/ruby_smb/smb1/bit_field/security_mode.rb +16 -0
  34. data/lib/ruby_smb/smb1/bit_field.rb +10 -0
  35. data/lib/ruby_smb/smb1/commands.rb +9 -0
  36. data/lib/ruby_smb/smb1/data_block.rb +42 -0
  37. data/lib/ruby_smb/smb1/dialect.rb +11 -0
  38. data/lib/ruby_smb/smb1/packet/error_packet.rb +14 -0
  39. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +52 -0
  40. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +46 -0
  41. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +47 -0
  42. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +71 -0
  43. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +48 -0
  44. data/lib/ruby_smb/smb1/packet.rb +12 -0
  45. data/lib/ruby_smb/smb1/parameter_block.rb +42 -0
  46. data/lib/ruby_smb/smb1/smb_header.rb +21 -0
  47. data/lib/ruby_smb/smb1.rb +16 -0
  48. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +17 -0
  49. data/lib/ruby_smb/smb2/bit_field/smb2_capabailities.rb +23 -0
  50. data/lib/ruby_smb/smb2/bit_field/smb2_header_flags.rb +23 -0
  51. data/lib/ruby_smb/smb2/bit_field/smb2_security_mode.rb +15 -0
  52. data/lib/ruby_smb/smb2/bit_field/smb2_security_mode_single.rb +14 -0
  53. data/lib/ruby_smb/smb2/bit_field.rb +11 -0
  54. data/lib/ruby_smb/smb2/commands.rb +25 -0
  55. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +50 -0
  56. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +33 -0
  57. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +53 -0
  58. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +38 -0
  59. data/lib/ruby_smb/smb2/packet.rb +10 -0
  60. data/lib/ruby_smb/smb2/smb2_header.rb +22 -0
  61. data/lib/ruby_smb/smb2.rb +12 -0
  62. data/lib/ruby_smb/version.rb +3 -0
  63. data/lib/ruby_smb.rb +22 -0
  64. data/ruby_smb.gemspec +38 -0
  65. data/spec/lib/ruby_smb/client_spec.rb +638 -0
  66. data/spec/lib/ruby_smb/dispatcher/dispatcher_base_spec.rb +22 -0
  67. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +60 -0
  68. data/spec/lib/ruby_smb/field/file_time_spec.rb +59 -0
  69. data/spec/lib/ruby_smb/field/nt_status_spec.rb +19 -0
  70. data/spec/lib/ruby_smb/field/stringz16_spec.rb +50 -0
  71. data/spec/lib/ruby_smb/generic_packet_spec.rb +58 -0
  72. data/spec/lib/ruby_smb/smb1/andx_block_spec.rb +41 -0
  73. data/spec/lib/ruby_smb/smb1/bit_field/capabilities_spec.rb +245 -0
  74. data/spec/lib/ruby_smb/smb1/bit_field/header_flags2_spec.rb +146 -0
  75. data/spec/lib/ruby_smb/smb1/bit_field/header_flags_spec.rb +102 -0
  76. data/spec/lib/ruby_smb/smb1/bit_field/security_mode_spec.rb +44 -0
  77. data/spec/lib/ruby_smb/smb1/data_block_spec.rb +26 -0
  78. data/spec/lib/ruby_smb/smb1/dialect_spec.rb +26 -0
  79. data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +39 -0
  80. data/spec/lib/ruby_smb/smb1/packet/negotiate_request_spec.rb +77 -0
  81. data/spec/lib/ruby_smb/smb1/packet/negotiate_response_extended_spec.rb +149 -0
  82. data/spec/lib/ruby_smb/smb1/packet/negotiate_response_spec.rb +150 -0
  83. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +100 -0
  84. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +72 -0
  85. data/spec/lib/ruby_smb/smb1/parameter_block_spec.rb +26 -0
  86. data/spec/lib/ruby_smb/smb1/smb_header_spec.rb +96 -0
  87. data/spec/lib/ruby_smb/smb2/bit_field/header_flags_spec.rb +81 -0
  88. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +28 -0
  89. data/spec/lib/ruby_smb/smb2/bit_field/smb2_capabilities_spec.rb +72 -0
  90. data/spec/lib/ruby_smb/smb2/bit_field/smb_secruity_mode_spec.rb +22 -0
  91. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +122 -0
  92. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +147 -0
  93. data/spec/lib/ruby_smb/smb2/packet/session_setup_request_spec.rb +79 -0
  94. data/spec/lib/ruby_smb/smb2/packet/session_setup_response_spec.rb +54 -0
  95. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +127 -0
  96. data/spec/lib/ruby_smb_spec.rb +2 -0
  97. data/spec/spec_helper.rb +100 -0
  98. data/spec/support/mock_socket_dispatcher.rb +8 -0
  99. data/spec/support/shared/examples/bit_field_single_flag.rb +14 -0
  100. data.tar.gz.sig +0 -0
  101. metadata +384 -0
  102. metadata.gz.sig +0 -0
@@ -0,0 +1,638 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::Client do
4
+
5
+ let(:dispatcher) { RubySMB::Dispatcher::Socket.new(nil) }
6
+ let(:username) { 'msfadmin' }
7
+ let(:password) { 'msfadmin' }
8
+ subject(:client) { described_class.new(dispatcher, username: username, password: password) }
9
+ let(:smb1_client) { described_class.new(dispatcher, smb2:false, username: username, password: password) }
10
+ let(:smb2_client) { described_class.new(dispatcher, smb1:false, username: username, password: password) }
11
+
12
+ describe '#initialize' do
13
+ it 'should raise an ArgumentError without a valid dispatcher' do
14
+ expect{ described_class.new(nil) }.to raise_error(ArgumentError)
15
+ end
16
+
17
+ it 'defaults to true for SMB1 support' do
18
+ expect(client.smb1).to be true
19
+ end
20
+
21
+ it 'defaults to true for SMB2 support' do
22
+ expect(client.smb1).to be true
23
+ end
24
+
25
+ it 'accepts an argument to disable smb1 support' do
26
+ smb_client = described_class.new(dispatcher, smb1:false, username: username, password: password)
27
+ expect(smb_client.smb1).to be false
28
+ end
29
+
30
+ it 'accepts an argument to disable smb2 support' do
31
+ expect(smb1_client.smb2).to be false
32
+ end
33
+
34
+ it 'raises an exception if both SMB1 and SMB2 are disabled' do
35
+ expect{described_class.new(dispatcher, smb1:false, smb2:false, username: username, password: password)}.to raise_error(ArgumentError, 'You must enable at least one Protocol')
36
+ end
37
+
38
+ it 'sets the username attribute' do
39
+ expect(client.username).to eq username
40
+ end
41
+
42
+ it 'sets the password attribute' do
43
+ expect(client.password).to eq password
44
+ end
45
+
46
+ it 'crates an NTLM client' do
47
+ expect(client.ntlm_client).to be_a Net::NTLM::Client
48
+ end
49
+ end
50
+
51
+ describe '#send_recv' do
52
+ let(:smb1_request) { RubySMB::SMB1::Packet::SessionSetupRequest.new }
53
+ let(:smb2_request) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
54
+
55
+ before(:each) do
56
+ expect(dispatcher).to receive(:send_packet).and_return(nil)
57
+ expect(dispatcher).to receive(:recv_packet).and_return("A")
58
+ end
59
+
60
+ it 'checks the packet version' do
61
+ expect(smb1_request).to receive(:packet_smb_version).and_call_original
62
+ client.send_recv(smb1_request)
63
+ end
64
+
65
+ it 'calls #smb1_sign if it is an SMB1 packet' do
66
+ expect(client).to receive(:smb1_sign).with(smb1_request).and_call_original
67
+ client.send_recv(smb1_request)
68
+ end
69
+
70
+ it 'calls #smb2_sign if it is an SMB2 packet' do
71
+ expect(client).to receive(:smb2_sign).with(smb2_request).and_call_original
72
+ client.send_recv(smb2_request)
73
+ end
74
+
75
+ end
76
+
77
+ describe '#login' do
78
+ before(:each) do
79
+ allow(client).to receive(:negotiate)
80
+ allow(client).to receive(:authenticate)
81
+ end
82
+
83
+ it 'defaults username to what was in the initializer' do
84
+ expect{ client.login }.to_not change(client, :username)
85
+ end
86
+
87
+ it 'overrides username if it is passed as a parameter' do
88
+ expect{ client.login(username:'test') }.to change(client, :username).to('test')
89
+ end
90
+
91
+ it 'defaults password to what was in the initializer' do
92
+ expect{ client.login }.to_not change(client, :password)
93
+ end
94
+
95
+ it 'overrides password if it is passed as a parameter' do
96
+ expect{ client.login(password:'test') }.to change(client, :password).to('test')
97
+ end
98
+
99
+ it 'defaults domain to what was in the initializer' do
100
+ expect{ client.login }.to_not change(client, :domain)
101
+ end
102
+
103
+ it 'overrides domain if it is passed as a parameter' do
104
+ expect{ client.login(domain:'test') }.to change(client, :domain).to('test')
105
+ end
106
+
107
+ it 'defaults local_workstation to what was in the initializer' do
108
+ expect{ client.login }.to_not change(client, :local_workstation)
109
+ end
110
+
111
+ it 'overrides local_workstation if it is passed as a parameter' do
112
+ expect{ client.login(local_workstation:'test') }.to change(client, :local_workstation).to('test')
113
+ end
114
+
115
+ it 'initialises a new NTLM Client' do
116
+ expect{ client.login }.to change(client, :ntlm_client)
117
+ end
118
+
119
+ it 'calls negotiate after the setup' do
120
+ expect(client).to receive(:negotiate)
121
+ client.login
122
+ end
123
+
124
+ it 'calls authenticate after negotiate' do
125
+ expect(client).to receive(:authenticate)
126
+ client.login
127
+ end
128
+
129
+ end
130
+
131
+ context 'Protocol Negotiation' do
132
+ let(:random_junk) { "fgrgrwgawrtw4t4tg4gahgn" }
133
+ let(:smb1_capabilities) {
134
+ {:level_2_oplocks=>1,
135
+ :nt_status=>1,
136
+ :rpc_remote_apis=>1,
137
+ :nt_smbs=>1,
138
+ :large_files=>1,
139
+ :unicode=>1,
140
+ :mpx_mode=>0,
141
+ :raw_mode=>0,
142
+ :large_writex=>1,
143
+ :large_readx=>1,
144
+ :info_level_passthru=>1,
145
+ :dfs=>0,
146
+ :reserved1=>0,
147
+ :bulk_transfer=>0,
148
+ :nt_find=>1,
149
+ :lock_and_read=>1,
150
+ :unix=>0,
151
+ :reserved2=>0,
152
+ :lwio=>1,
153
+ :extended_security=>1,
154
+ :reserved3=>0,
155
+ :dynamic_reauth=>0,
156
+ :reserved4=>0,
157
+ :compressed_data=>0,
158
+ :reserved5=>0}
159
+ }
160
+ let(:smb1_extended_response) {
161
+ packet = RubySMB::SMB1::Packet::NegotiateResponseExtended.new
162
+ packet.parameter_block.capabilities = smb1_capabilities
163
+ packet
164
+ }
165
+ let(:smb1_extended_response_raw) {
166
+ smb1_extended_response.to_binary_s
167
+ }
168
+
169
+ let(:smb2_response) { RubySMB::SMB2::Packet::NegotiateResponse.new }
170
+
171
+ describe '#smb1_negotiate_request' do
172
+ it 'returns an SMB1 Negotiate Request packet' do
173
+ expect(client.smb1_negotiate_request).to be_a(RubySMB::SMB1::Packet::NegotiateRequest)
174
+ end
175
+
176
+ it 'sets the default SMB1 Dialect' do
177
+ expect(client.smb1_negotiate_request.dialects).to include({:buffer_format=>2, :dialect_string=> RubySMB::Client::SMB1_DIALECT_SMB1_DEFAULT})
178
+ end
179
+
180
+ it 'sets the SMB2.02 dialect if SMB2 support is enabled' do
181
+ expect(client.smb1_negotiate_request.dialects).to include({:buffer_format=>2, :dialect_string=> RubySMB::Client::SMB1_DIALECT_SMB2_DEFAULT})
182
+ end
183
+
184
+ it 'excludes the SMB2.02 Dialect if SMB2 support is disabled' do
185
+ expect(smb1_client.smb1_negotiate_request.dialects).to_not include({:buffer_format=>2, :dialect_string=> RubySMB::Client::SMB1_DIALECT_SMB2_DEFAULT})
186
+ end
187
+
188
+ it 'excludes the default SMB1 Dialect if SMB1 support is disabled' do
189
+ expect(smb2_client.smb1_negotiate_request.dialects).to_not include({:buffer_format=>2, :dialect_string=> RubySMB::Client::SMB1_DIALECT_SMB1_DEFAULT})
190
+ end
191
+ end
192
+
193
+ describe '#smb2_negotiate_request' do
194
+ it 'return an SMB2 Negotiate Request packet' do
195
+ expect(client.smb2_negotiate_request).to be_a(RubySMB::SMB2::Packet::NegotiateRequest)
196
+ end
197
+
198
+ it 'sets the default SMB2 Dialect' do
199
+ expect(client.smb2_negotiate_request.dialects).to include(RubySMB::Client::SMB2_DIALECT_DEFAULT)
200
+ end
201
+
202
+ it 'sets the Message ID to 0' do
203
+ expect(client.smb2_negotiate_request.smb2_header.message_id).to eq 0
204
+ end
205
+ end
206
+
207
+ describe '#negotiate_request' do
208
+ it 'calls #smb1_negotiate_request if SMB1 is enabled' do
209
+ expect(smb1_client).to receive(:smb1_negotiate_request)
210
+ expect(smb1_client).to receive(:send_recv)
211
+ smb1_client.negotiate_request
212
+ end
213
+
214
+ it 'calls #smb1_negotiate_request if both protocols are enabled' do
215
+ expect(client).to receive(:smb1_negotiate_request)
216
+ expect(client).to receive(:send_recv)
217
+ client.negotiate_request
218
+ end
219
+
220
+ it 'calls #smb2_negotiate_request if SMB2 is enabled' do
221
+ expect(smb2_client).to receive(:smb2_negotiate_request)
222
+ expect(smb2_client).to receive(:send_recv)
223
+ smb2_client.negotiate_request
224
+ end
225
+
226
+ it 'returns the raw response string from the server' do
227
+ expect(client).to receive(:send_recv).and_return('A')
228
+ expect(client.negotiate_request).to eq "A"
229
+ end
230
+ end
231
+
232
+ describe '#negotiate_response' do
233
+ context 'with only SMB1' do
234
+ it 'returns a properly formed packet' do
235
+ expect(smb1_client.negotiate_response(smb1_extended_response_raw)).to eq smb1_extended_response
236
+ end
237
+
238
+ it 'raises an exception if the Response is invalid' do
239
+ expect{ smb1_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
240
+ end
241
+
242
+ it 'considers the response invalid if it is not an actual Negotiate Response' do
243
+ bogus_response = smb1_extended_response
244
+ bogus_response.smb_header.command = 0xff
245
+ expect{ smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
246
+ end
247
+
248
+ it 'considers the response invalid if Extended Security is not enabled' do
249
+ bogus_response = smb1_extended_response
250
+ bogus_response.parameter_block.capabilities.extended_security = 0
251
+ expect{ smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
252
+ end
253
+ end
254
+
255
+ context 'with only SMB2' do
256
+ it 'returns a properly formed packet' do
257
+ expect( smb2_client.negotiate_response(smb2_response.to_binary_s) ).to eq smb2_response
258
+ end
259
+
260
+ it 'raises an exception if the Response is invalid' do
261
+ expect{ smb2_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
262
+ end
263
+ end
264
+
265
+ context 'with SMB1 and SMB2 enabled' do
266
+ it 'returns an SMB1 NegotiateResponse if it looks like SMB1' do
267
+ expect( client.negotiate_response(smb1_extended_response_raw) ).to eq smb1_extended_response
268
+ end
269
+
270
+ it 'returns an SMB2 NegotiateResponse if it looks like SMB2' do
271
+ expect( client.negotiate_response(smb2_response.to_binary_s) ).to eq smb2_response
272
+ end
273
+ end
274
+ end
275
+
276
+ describe '#parse_negotiate_response' do
277
+ context 'when SMB1 was Negotiated' do
278
+ it 'turns off SMB2 support' do
279
+ client.parse_negotiate_response(smb1_extended_response)
280
+ expect( client.smb2 ).to be false
281
+ end
282
+
283
+ it 'sets whether or not signing is required' do
284
+ smb1_extended_response.parameter_block.security_mode.security_signatures_required = 1
285
+ client.parse_negotiate_response(smb1_extended_response)
286
+ expect(client.signing_required).to be true
287
+ end
288
+ end
289
+
290
+ context 'when SMB2 was negotiated' do
291
+ it 'turns off SMB1 support' do
292
+ client.parse_negotiate_response(smb2_response)
293
+ expect( client.smb1 ).to be false
294
+ end
295
+
296
+ it 'sets whether or not signing is required' do
297
+ smb2_response.security_mode.signing_required = 1
298
+ client.parse_negotiate_response(smb2_response)
299
+ expect(client.signing_required).to be true
300
+ end
301
+ end
302
+ end
303
+
304
+ describe '#negotiate' do
305
+ it 'calls the backing methods' do
306
+ expect(client).to receive(:negotiate_request)
307
+ expect(client).to receive(:negotiate_response)
308
+ expect(client).to receive(:parse_negotiate_response)
309
+ client.negotiate
310
+ end
311
+ end
312
+ end
313
+
314
+ context 'Authentication' do
315
+ let(:type2_string) {
316
+ "TlRMTVNTUAACAAAAHgAeADgAAAA1goriwmZ8HEHtFHAAAAAAAAAAAJgAmABW\nAAAABgGxHQAAAA" +
317
+ "9XAEkATgAtAFMATgBKAEQARwAwAFUAQQA5ADAARgACAB4A\nVwBJAE4ALQBTAE4ASgBEAEcAMABV" +
318
+ "AEEAOQAwAEYAAQAeAFcASQBOAC0AUwBO\nAEoARABHADAAVQBBADkAMABGAAQAHgBXAEkATgAtAF" +
319
+ "MATgBKAEQARwAwAFUA\nQQA5ADAARgADAB4AVwBJAE4ALQBTAE4ASgBEAEcAMABVAEEAOQAwAEYABw" +
320
+ "AI\nADxThZ4nnNIBAAAAAA==\n"
321
+ }
322
+ context 'for SMB1' do
323
+ let(:ntlm_client) { smb1_client.ntlm_client }
324
+ let(:type1_message) { ntlm_client.init_context }
325
+ let(:negotiate_packet) { RubySMB::SMB1::Packet::SessionSetupRequest.new }
326
+ let(:type3_message) { ntlm_client.init_context(type2_string) }
327
+ let(:user_id) { 2041 }
328
+
329
+
330
+ describe '#smb1_ntlmssp_auth_packet' do
331
+ it 'creates a new SessionSetupRequest packet' do
332
+ expect(RubySMB::SMB1::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
333
+ smb1_client.smb1_ntlmssp_auth_packet(type2_string, user_id)
334
+ end
335
+
336
+ it 'builds the security blob with an NTLM Type 3 Message' do
337
+ expect(RubySMB::SMB1::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
338
+ expect(ntlm_client).to receive(:init_context).with(type2_string).and_return(type3_message)
339
+ expect(negotiate_packet).to receive(:set_type3_blob).with(type3_message.serialize)
340
+ smb1_client.smb1_ntlmssp_auth_packet(type2_string, user_id)
341
+ end
342
+
343
+ it 'enables extended security on the packet' do
344
+ expect(smb1_client.smb1_ntlmssp_auth_packet(type2_string, user_id).smb_header.flags2.extended_security).to eq 1
345
+ end
346
+ end
347
+
348
+ describe '#smb1_ntlmssp_negotiate_packet' do
349
+ it 'creates a new SessionSetupRequest packet' do
350
+ expect(RubySMB::SMB1::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
351
+ smb1_client.smb1_ntlmssp_negotiate_packet
352
+ end
353
+
354
+ it 'builds the security blob with an NTLM Type 1 Message' do
355
+ expect(RubySMB::SMB1::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
356
+ expect(ntlm_client).to receive(:init_context).and_return(type1_message)
357
+ expect(negotiate_packet).to receive(:set_type1_blob).with(type1_message.serialize)
358
+ smb1_client.smb1_ntlmssp_negotiate_packet
359
+ end
360
+
361
+ it 'enables extended security on the packet' do
362
+ expect(smb1_client.smb1_ntlmssp_negotiate_packet.smb_header.flags2.extended_security).to eq 1
363
+ end
364
+ end
365
+
366
+ describe '#smb1_ntlmssp_authenticate' do
367
+ it 'sends the request packet and receives a response' do
368
+ expect(smb1_client).to receive(:smb1_ntlmssp_auth_packet).and_return(negotiate_packet)
369
+ expect(dispatcher).to receive(:send_packet).with(negotiate_packet)
370
+ expect(dispatcher).to receive(:recv_packet)
371
+ smb1_client.smb1_ntlmssp_authenticate(type2_string, user_id)
372
+ end
373
+ end
374
+
375
+ describe '#smb1_ntlmssp_negotiate' do
376
+ it 'sends the request packet and receives a response' do
377
+ expect(smb1_client).to receive(:smb1_ntlmssp_negotiate_packet).and_return(negotiate_packet)
378
+ expect(dispatcher).to receive(:send_packet).with(negotiate_packet)
379
+ expect(dispatcher).to receive(:recv_packet)
380
+ smb1_client.smb1_ntlmssp_negotiate
381
+ end
382
+ end
383
+
384
+ describe '#smb1_ntlmssp_challenge_packet' do
385
+ let(:response) {
386
+ packet = RubySMB::SMB1::Packet::SessionSetupResponse.new
387
+ packet.smb_header.nt_status = 0xc0000016
388
+ packet
389
+ }
390
+ let(:wrong_command) {
391
+ packet = RubySMB::SMB1::Packet::SessionSetupResponse.new
392
+ packet.smb_header.nt_status = 0xc0000016
393
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
394
+ packet
395
+ }
396
+ it 'returns the packet object' do
397
+ expect(smb1_client.smb1_ntlmssp_challenge_packet(response.to_binary_s)).to eq response
398
+ end
399
+
400
+ it 'raises an UnexpectedStatusCode if the status code is not correct' do
401
+ response.smb_header.nt_status = 0xc0000015
402
+ expect{ smb1_client.smb1_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error( RubySMB::Error::UnexpectedStatusCode)
403
+ end
404
+
405
+ it 'raises an InvalidPacket if the Command field is wrong' do
406
+ expect{ smb1_client.smb1_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
407
+ end
408
+ end
409
+
410
+ describe '#smb1_ntlmssp_final_packet' do
411
+ let(:response) {
412
+ packet = RubySMB::SMB1::Packet::SessionSetupResponse.new
413
+ packet.smb_header.nt_status = 0x00000000
414
+ packet
415
+ }
416
+ let(:wrong_command) {
417
+ packet = RubySMB::SMB1::Packet::SessionSetupResponse.new
418
+ packet.smb_header.nt_status = 0x00000000
419
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
420
+ packet
421
+ }
422
+ it 'returns the packet object' do
423
+ expect(smb1_client.smb1_ntlmssp_final_packet(response.to_binary_s)).to eq response
424
+ end
425
+
426
+ it 'raises an InvalidPacket if the Command field is wrong' do
427
+ expect{ smb1_client.smb1_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
428
+ end
429
+ end
430
+
431
+ describe '#smb1_type2_message' do
432
+ let(:fake_type2) { "NTLMSSP FOO" }
433
+ let(:response_packet) {
434
+ packet = RubySMB::SMB1::Packet::SessionSetupResponse.new
435
+ packet.set_type2_blob(fake_type2)
436
+ packet
437
+ }
438
+ it 'returns a base64 encoded copy of the Type 2 NTLM message' do
439
+ expect(smb1_client.smb1_type2_message(response_packet)).to eq [fake_type2].pack('m')
440
+ end
441
+ end
442
+ end
443
+
444
+ context 'for SMB2' do
445
+ let(:ntlm_client) { smb2_client.ntlm_client }
446
+ let(:type1_message) { ntlm_client.init_context }
447
+ let(:negotiate_packet) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
448
+ let(:type3_message) { ntlm_client.init_context(type2_string) }
449
+ let(:session_id) { 0x0000040000000005 }
450
+
451
+ describe '#smb2_ntlmssp_negotiate_packet' do
452
+ it 'creates a new SessionSetupRequest packet' do
453
+ expect(RubySMB::SMB2::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
454
+ smb2_client.smb2_ntlmssp_negotiate_packet
455
+ end
456
+
457
+ it 'builds the security blob with an NTLM Type 1 Message' do
458
+ expect(RubySMB::SMB2::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
459
+ expect(ntlm_client).to receive(:init_context).and_return(type1_message)
460
+ expect(negotiate_packet).to receive(:set_type1_blob).with(type1_message.serialize)
461
+ smb2_client.smb2_ntlmssp_negotiate_packet
462
+ end
463
+
464
+ it 'sets the message ID in the packet header to 1' do
465
+ expect(smb2_client.smb2_ntlmssp_negotiate_packet.smb2_header.message_id).to eq 1
466
+ end
467
+
468
+ it 'increments client#smb2_message_id' do
469
+ expect{ smb2_client.smb2_ntlmssp_negotiate_packet }.to change(smb2_client, :smb2_message_id).to(2)
470
+ end
471
+ end
472
+
473
+ describe '#smb2_ntlmssp_negotiate' do
474
+ it 'sends the request packet and receives a response' do
475
+ expect(smb2_client).to receive(:smb2_ntlmssp_negotiate_packet).and_return(negotiate_packet)
476
+ expect(dispatcher).to receive(:send_packet).with(negotiate_packet)
477
+ expect(dispatcher).to receive(:recv_packet)
478
+ smb2_client.smb2_ntlmssp_negotiate
479
+ end
480
+ end
481
+
482
+ describe '#smb2_ntlmssp_challenge_packet' do
483
+ let(:response) {
484
+ packet = RubySMB::SMB2::Packet::SessionSetupResponse.new
485
+ packet.smb2_header.nt_status = 0xc0000016
486
+ packet
487
+ }
488
+ let(:wrong_command) {
489
+ packet = RubySMB::SMB2::Packet::SessionSetupResponse.new
490
+ packet.smb2_header.nt_status = 0xc0000016
491
+ packet.smb2_header.command = RubySMB::SMB2::Commands::NEGOTIATE
492
+ packet
493
+ }
494
+ it 'returns the packet object' do
495
+ expect(smb2_client.smb2_ntlmssp_challenge_packet(response.to_binary_s)).to eq response
496
+ end
497
+
498
+ it 'raises an UnexpectedStatusCode if the status code is not correct' do
499
+ response.smb2_header.nt_status = 0xc0000015
500
+ expect{ smb2_client.smb2_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error( RubySMB::Error::UnexpectedStatusCode)
501
+ end
502
+
503
+ it 'raises an InvalidPacket if the Command field is wrong' do
504
+ expect{ smb2_client.smb2_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
505
+ end
506
+ end
507
+
508
+ describe '#smb2_type2_message' do
509
+ let(:fake_type2) { "NTLMSSP FOO" }
510
+ let(:response_packet) {
511
+ packet = RubySMB::SMB2::Packet::SessionSetupResponse.new
512
+ packet.set_type2_blob(fake_type2)
513
+ packet
514
+ }
515
+ it 'returns a base64 encoded copy of the Type 2 NTLM message' do
516
+ expect(smb2_client.smb2_type2_message(response_packet)).to eq [fake_type2].pack('m')
517
+ end
518
+ end
519
+
520
+ describe '#smb1_ntlmssp_auth_packet' do
521
+ it 'creates a new SessionSetupRequest packet' do
522
+ expect(RubySMB::SMB2::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
523
+ smb2_client.smb2_ntlmssp_auth_packet(type2_string, session_id)
524
+ end
525
+
526
+ it 'builds the security blob with an NTLM Type 3 Message' do
527
+ expect(RubySMB::SMB2::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
528
+ expect(ntlm_client).to receive(:init_context).with(type2_string).and_return(type3_message)
529
+ expect(negotiate_packet).to receive(:set_type3_blob).with(type3_message.serialize)
530
+ smb2_client.smb2_ntlmssp_auth_packet(type2_string, session_id)
531
+ end
532
+
533
+ it 'sets the session ID on the request packet' do
534
+ expect(smb2_client.smb2_ntlmssp_auth_packet(type2_string, session_id).smb2_header.session_id).to eq session_id
535
+ end
536
+
537
+ end
538
+
539
+ describe '#smb2_ntlmssp_authenticate' do
540
+ it 'sends the request packet and receives a response' do
541
+ expect(smb2_client).to receive(:smb2_ntlmssp_auth_packet).and_return(negotiate_packet)
542
+ expect(dispatcher).to receive(:send_packet).with(negotiate_packet)
543
+ expect(dispatcher).to receive(:recv_packet)
544
+ smb2_client.smb2_ntlmssp_authenticate(type2_string, session_id)
545
+ end
546
+ end
547
+
548
+ describe '#smb2_ntlmssp_final_packet' do
549
+ let(:response) {
550
+ packet = RubySMB::SMB2::Packet::SessionSetupResponse.new
551
+ packet.smb2_header.nt_status = 0x00000000
552
+ packet
553
+ }
554
+ let(:wrong_command) {
555
+ packet = RubySMB::SMB2::Packet::SessionSetupResponse.new
556
+ packet.smb2_header.nt_status = 0x00000000
557
+ packet.smb2_header.command = RubySMB::SMB2::Commands::NEGOTIATE
558
+ packet
559
+ }
560
+ it 'returns the packet object' do
561
+ expect(smb2_client.smb2_ntlmssp_final_packet(response.to_binary_s)).to eq response
562
+ end
563
+
564
+ it 'raises an InvalidPacket if the Command field is wrong' do
565
+ expect{ smb2_client.smb2_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
566
+ end
567
+ end
568
+ end
569
+ end
570
+
571
+ context 'Signing' do
572
+ describe '#smb2_sign' do
573
+ let(:request1) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
574
+ let(:fake_hmac) { "\x31\x07\x78\x3e\x35\xd7\x0e\x89\x08\x43\x8a\x18\xcd\x78\x52\x39".force_encoding("ASCII-8BIT") }
575
+
576
+ context 'if signing is required and we have a session key' do
577
+ it 'generates the HMAC based on the packet and the NTLM session key and signs the packet with it' do
578
+ smb2_client.session_key = 'foo'
579
+ smb2_client.signing_required = true
580
+ expect(OpenSSL::HMAC).to receive(:digest).with(instance_of(OpenSSL::Digest::SHA256),smb2_client.session_key,request1.to_binary_s).and_return(fake_hmac)
581
+ expect(smb2_client.smb2_sign(request1).smb2_header.signature).to eq fake_hmac
582
+ end
583
+ end
584
+
585
+ context 'when signing is not required' do
586
+ it 'returns the packet exactly as it was given' do
587
+ smb2_client.session_key = 'foo'
588
+ smb2_client.signing_required = false
589
+ expect(smb2_client.smb2_sign(request1)).to eq request1
590
+ end
591
+ end
592
+
593
+ context 'when there is no session_key' do
594
+ it 'returns the packet exactly as it was given' do
595
+ smb2_client.session_key = ''
596
+ smb2_client.signing_required = true
597
+ expect(smb2_client.smb2_sign(request1)).to eq request1
598
+ end
599
+ end
600
+
601
+ end
602
+
603
+ describe '#smb1_sign' do
604
+ let(:request1) { RubySMB::SMB1::Packet::SessionSetupRequest.new }
605
+ let(:fake_sig) { "\x9f\x62\xcf\x08\xd9\xc2\x83\x21".force_encoding("ASCII-8BIT") }
606
+
607
+ context 'if signing is required and we have a session key' do
608
+ it 'generates the signature based on the packet, the sequence counter and the NTLM session key and signs the packet with it' do
609
+ smb1_client.session_key = 'foo'
610
+ smb1_client.signing_required = true
611
+ raw = request1.to_binary_s
612
+ adjusted_request = RubySMB::SMB1::Packet::SessionSetupRequest.read(raw)
613
+ adjusted_request.smb_header.security_features = smb1_client.sequence_counter
614
+ expect(OpenSSL::Digest::MD5).to receive(:digest).with(smb1_client.session_key + adjusted_request.to_binary_s).and_return(fake_sig)
615
+ expect(smb1_client.smb1_sign(request1).smb_header.security_features).to eq fake_sig
616
+ end
617
+ end
618
+
619
+ context 'when signing is not required' do
620
+ it 'returns the packet exactly as it was given' do
621
+ smb1_client.session_key = 'foo'
622
+ smb1_client.signing_required = false
623
+ expect(smb1_client.smb1_sign(request1)).to eq request1
624
+ end
625
+ end
626
+
627
+ context 'when there is no session_key' do
628
+ it 'returns the packet exactly as it was given' do
629
+ smb1_client.session_key = ''
630
+ smb1_client.signing_required = true
631
+ expect(smb1_client.smb1_sign(request1)).to eq request1
632
+ end
633
+ end
634
+ end
635
+ end
636
+
637
+
638
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::Dispatcher::Base do
4
+
5
+ subject(:dispatcher) { described_class.new }
6
+
7
+ describe '#nbss' do
8
+ it 'returns the size of the packet to the packet in 4 bytes' do
9
+ packet = RubySMB::SMB1::Packet::NegotiateRequest.new
10
+ packet_size = packet.do_num_bytes
11
+ expect( dispatcher.nbss(packet) ).to eq "\x00\x00\x00\x23"
12
+ end
13
+ end
14
+
15
+ it 'raises NotImplementedError on #send_packet' do
16
+ expect{ dispatcher.send_packet("foo") }.to raise_error(NotImplementedError)
17
+ end
18
+
19
+ it 'raises NotImplementedError on #recv_packet' do
20
+ expect{ dispatcher.recv_packet }.to raise_error(NotImplementedError)
21
+ end
22
+ end