ruby_smb 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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