ruby_smb 1.0.5 → 1.1.0

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 (77) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +4 -1
  5. data/README.md +35 -47
  6. data/examples/enum_registry_key.rb +28 -0
  7. data/examples/enum_registry_values.rb +30 -0
  8. data/examples/pipes.rb +2 -1
  9. data/examples/read_registry_key_value.rb +32 -0
  10. data/lib/ruby_smb.rb +0 -1
  11. data/lib/ruby_smb/client.rb +2 -0
  12. data/lib/ruby_smb/client/winreg.rb +46 -0
  13. data/lib/ruby_smb/dcerpc.rb +38 -0
  14. data/lib/ruby_smb/dcerpc/bind.rb +2 -2
  15. data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
  16. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  17. data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
  18. data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
  19. data/lib/ruby_smb/dcerpc/request.rb +28 -9
  20. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
  21. data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
  22. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
  23. data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
  24. data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
  25. data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
  26. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
  27. data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
  28. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
  29. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
  30. data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
  31. data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
  32. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
  33. data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
  34. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
  35. data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
  36. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
  37. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
  38. data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
  39. data/lib/ruby_smb/smb1/file.rb +2 -0
  40. data/lib/ruby_smb/smb1/pipe.rb +78 -2
  41. data/lib/ruby_smb/smb2/packet/error_packet.rb +2 -4
  42. data/lib/ruby_smb/smb2/pipe.rb +89 -2
  43. data/lib/ruby_smb/version.rb +1 -1
  44. data/ruby_smb.gemspec +3 -3
  45. data/spec/lib/ruby_smb/client_spec.rb +148 -0
  46. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
  47. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
  48. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
  49. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
  50. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
  51. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
  52. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
  53. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
  54. data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
  55. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
  56. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
  57. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
  58. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
  59. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
  60. data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
  61. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
  62. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
  63. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
  64. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
  65. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
  66. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
  67. data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
  68. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
  69. data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
  70. data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
  71. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +210 -148
  72. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +3 -24
  73. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +256 -145
  74. metadata +66 -9
  75. metadata.gz.sig +0 -0
  76. data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
  77. data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -0,0 +1,81 @@
1
+ RSpec.describe RubySMB::Dcerpc do
2
+ let(:tree) { double('Tree') }
3
+ let(:pipe) do
4
+ RubySMB::SMB1::Pipe.new(
5
+ tree: tree,
6
+ response: RubySMB::SMB1::Packet::NtCreateAndxResponse.new,
7
+ name: 'winreg'
8
+ )
9
+ end
10
+
11
+ describe '#bind' do
12
+ let(:options) { { endpoint: RubySMB::Dcerpc::Winreg } }
13
+ let(:bind_packet) { RubySMB::Dcerpc::Bind.new(options) }
14
+ let(:bind_ack_packet) { RubySMB::Dcerpc::BindAck.new }
15
+ let(:client) { double('Client') }
16
+
17
+ before :example do
18
+ allow(RubySMB::Dcerpc::Bind).to receive(:new).and_return(bind_packet)
19
+ allow(pipe).to receive(:write)
20
+ allow(pipe).to receive(:read)
21
+ bind_ack_packet.p_result_list.n_results = 1
22
+ bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::ACCEPTANCE
23
+ allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(bind_ack_packet)
24
+ allow(tree).to receive(:client).and_return(client)
25
+ allow(client).to receive(:max_buffer_size=)
26
+ end
27
+
28
+ it 'creates a Bind packet' do
29
+ pipe.bind(options)
30
+ expect(RubySMB::Dcerpc::Bind).to have_received(:new).with(options)
31
+ end
32
+
33
+ it 'writes to the named pipe' do
34
+ pipe.bind(options)
35
+ expect(pipe).to have_received(:write).with(data: bind_packet.to_binary_s)
36
+ end
37
+
38
+ it 'reads the socket' do
39
+ pipe.bind(options)
40
+ expect(pipe).to have_received(:read)
41
+ end
42
+
43
+ it 'creates a BindAck packet from the response' do
44
+ raw_response = RubySMB::Dcerpc::BindAck.new.to_binary_s
45
+ allow(pipe).to receive(:read).and_return(raw_response)
46
+ pipe.bind(options)
47
+ expect(RubySMB::Dcerpc::BindAck).to have_received(:read).with(raw_response)
48
+ end
49
+
50
+ it 'raises the expected exception when an invalid packet is received' do
51
+ allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_raise(IOError)
52
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
53
+ end
54
+
55
+ it 'raises the expected exception when it is not a BindAck packet' do
56
+ response = RubySMB::Dcerpc::Bind.new
57
+ allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(response)
58
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
59
+ end
60
+
61
+ it 'raises an exception when no result is returned' do
62
+ bind_ack_packet.p_result_list.n_results = 0
63
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
64
+ end
65
+
66
+ it 'raises an exception when result is not ACCEPTANCE' do
67
+ bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::USER_REJECTION
68
+ expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
69
+ end
70
+
71
+ it 'sets the Tree #client.max_buffer_size property to the DCERPC response #max_xmit_frag property value' do
72
+ bind_ack_packet.max_xmit_frag = 64
73
+ pipe.bind(options)
74
+ expect(client).to have_received(:max_buffer_size=).with(64)
75
+ end
76
+
77
+ it 'returns the expected BindAck packet' do
78
+ expect(pipe.bind(options)).to eq(bind_ack_packet)
79
+ end
80
+ end
81
+ end
@@ -110,10 +110,18 @@ RSpec.describe RubySMB::SMB1::File do
110
110
  file.read_packet
111
111
  end
112
112
 
113
- it 'sets the read_length of the packet' do
113
+ it 'sets the #max_count_of_bytes_to_return of the packet' do
114
114
  expect(file.read_packet(read_length: 55).parameter_block.max_count_of_bytes_to_return).to eq 55
115
115
  end
116
116
 
117
+ it 'sets the #min_count_of_bytes_to_return of the packet' do
118
+ expect(file.read_packet(read_length: 55).parameter_block.min_count_of_bytes_to_return).to eq 55
119
+ end
120
+
121
+ it 'sets the #remaining of the packet' do
122
+ expect(file.read_packet(read_length: 55).parameter_block.remaining).to eq 55
123
+ end
124
+
117
125
  it 'sets the offset of the packet' do
118
126
  expect(file.read_packet(offset: 55).parameter_block.offset).to eq 55
119
127
  end
@@ -1,15 +1,12 @@
1
1
  RSpec.describe RubySMB::SMB1::Pipe do
2
2
 
3
+ it { expect(described_class).to be < RubySMB::SMB1::File }
4
+
3
5
  let(:peek_nmpipe_response) {
4
6
  packet = RubySMB::SMB1::Packet::Trans::PeekNmpipeResponse.new
5
7
  packet.data_block.trans_parameters.read("\x10\x20\x00\x00\x03\x00")
6
8
  packet
7
9
  }
8
-
9
- describe RubySMB::SMB1::Pipe do
10
- it { expect(described_class).to be < RubySMB::SMB1::File }
11
- end
12
-
13
10
  let(:dispatcher) { RubySMB::Dispatcher::Socket.new(double('socket')) }
14
11
  let(:client) { RubySMB::Client.new(dispatcher, username: 'msfadmin', password: 'msfadmin') }
15
12
  let(:connect_response) {
@@ -124,201 +121,266 @@ RSpec.describe RubySMB::SMB1::Pipe do
124
121
  end
125
122
  end
126
123
 
127
- context 'with DCERPC' do
128
- describe '#net_share_enum_all' do
129
- let(:host) { '1.2.3.4' }
130
- let(:dcerpc_response) { RubySMB::Dcerpc::Response.new }
131
-
132
- before :example do
133
- allow(pipe).to receive(:bind)
134
- allow(pipe).to receive(:request).and_return(dcerpc_response)
135
- allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return([])
124
+ describe '#initialize' do
125
+ context 'when name is not provided' do
126
+ it 'raises an ArgumentError' do
127
+ expect {
128
+ described_class.new(tree: tree, response: nt_create_andx_response, name: nil)
129
+ }.to raise_error(ArgumentError)
136
130
  end
131
+ end
137
132
 
138
- it 'calls #bind with the expected arguments' do
139
- expect(pipe).to receive(:bind).with(endpoint: RubySMB::Dcerpc::Srvsvc)
140
- pipe.net_share_enum_all(host)
141
- end
133
+ it 'calls the superclass with the expected arguments' do
134
+ expect(pipe.tree).to eq(tree)
135
+ expect(pipe.name).to eq(filename)
136
+ expect(pipe.attributes).to eq(nt_create_andx_response.parameter_block.ext_file_attributes)
137
+ expect(pipe.fid).to eq(nt_create_andx_response.parameter_block.fid)
138
+ expect(pipe.last_access).to eq(nt_create_andx_response.parameter_block.last_access_time.to_datetime)
139
+ expect(pipe.last_change).to eq(nt_create_andx_response.parameter_block.last_change_time.to_datetime)
140
+ expect(pipe.last_write).to eq(nt_create_andx_response.parameter_block.last_write_time.to_datetime)
141
+ expect(pipe.size).to eq(nt_create_andx_response.parameter_block.end_of_file)
142
+ expect(pipe.size_on_disk).to eq(nt_create_andx_response.parameter_block.allocation_size)
143
+ end
142
144
 
143
- it 'calls #request with the expected arguments' do
144
- expect(pipe).to receive(:request).with(RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL, host: host)
145
- pipe.net_share_enum_all(host)
145
+ context 'with \'srvsvc\' filename' do
146
+ it 'extends Srvsvc class' do
147
+ pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: 'srvsvc')
148
+ expect(pipe.respond_to?(:net_share_enum_all)).to be true
146
149
  end
150
+ end
147
151
 
148
- it 'parse the response with NetShareEnumAll #parse_response method' do
149
- stub = 'ABCD'
150
- dcerpc_response.alloc_hint = stub.size
151
- dcerpc_response.stub = stub
152
- expect(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).with(stub)
153
- pipe.net_share_enum_all(host)
152
+ context 'with \'winreg\' filename' do
153
+ it 'extends Winreg class' do
154
+ pipe = described_class.new(tree: tree, response: nt_create_andx_response, name: 'winreg')
155
+ expect(pipe.respond_to?(:has_registry_key?)).to be true
154
156
  end
157
+ end
158
+ end
155
159
 
156
- it 'returns the remote shares' do
157
- shares = [
158
- ["C$", "DISK", "Default share"],
159
- ["Shared", "DISK", ""],
160
- ["IPC$", "IPC", "Remote IPC"],
161
- ["ADMIN$", "DISK", "Remote Admin"]
162
- ]
163
- output = [
164
- {:name=>"C$", :type=>"DISK", :comment=>"Default share"},
165
- {:name=>"Shared", :type=>"DISK", :comment=>""},
166
- {:name=>"IPC$", :type=>"IPC", :comment=>"Remote IPC"},
167
- {:name=>"ADMIN$", :type=>"DISK", :comment=>"Remote Admin"},
168
- ]
169
- allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return(shares)
170
- expect(pipe.net_share_enum_all(host)).to eq(output)
171
- end
160
+ describe '#dcerpc_request' do
161
+ let(:options) { { host: '1.2.3.4' } }
162
+ let(:stub_packet ) { RubySMB::Dcerpc::Winreg::OpenKeyRequest.new }
163
+ let(:dcerpc_request) { double('DCERPC Request') }
164
+ let(:request_stub) { double('Request stub') }
165
+ let(:binary_dcerpc_request) { double('Binary DCERPC Request') }
166
+ let(:trans_nmpipe_request) { double('TransactNmpipeRequest') }
167
+ let(:trans_data) { double('Trans data') }
168
+ let(:trans_nmpipe_raw_response) { double('Trans nmpipe raw response') }
169
+ let(:trans_nmpipe_response) { double('TransactNmpipeResponse') }
170
+ let(:raw_data) { double('Raw data') }
171
+ let(:dcerpc_response) { double('DCERPC Response') }
172
+ let(:result) { 'Result' }
173
+
174
+ before :example do
175
+ allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(dcerpc_request)
176
+ allow(dcerpc_request).to receive_messages(
177
+ :stub => request_stub,
178
+ :to_binary_s => binary_dcerpc_request
179
+ )
180
+ allow(request_stub).to receive(:read)
181
+ allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to receive(:new).and_return(trans_nmpipe_request)
182
+ allow(tree).to receive(:set_header_fields)
183
+ allow(trans_nmpipe_request).to receive_message_chain(:data_block, :trans_data => trans_data)
184
+ allow(trans_nmpipe_request).to receive(:set_fid)
185
+ allow(trans_data).to receive(:write_data=)
186
+ allow(client).to receive(:send_recv).and_return(trans_nmpipe_raw_response)
187
+ allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).and_return(trans_nmpipe_response)
188
+ allow(trans_nmpipe_response).to receive_messages(
189
+ :valid? => true,
190
+ :status_code => WindowsError::NTStatus::STATUS_SUCCESS
191
+ )
192
+ allow(trans_nmpipe_response).to receive_message_chain(:data_block, :trans_data, :read_data, :to_binary_s => raw_data)
193
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(dcerpc_response)
194
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
195
+ allow(dcerpc_response).to receive(:stub).and_return(result)
172
196
  end
173
197
 
174
- describe '#bind' do
175
- let(:options) { { endpoint: RubySMB::Dcerpc::Srvsvc } }
176
- let(:bind_packet) { RubySMB::Dcerpc::Bind.new(options) }
177
- let(:bind_ack_packet) { RubySMB::Dcerpc::BindAck.new }
198
+ it 'creates a Request packet with the expected arguments' do
199
+ pipe.dcerpc_request(stub_packet, options)
200
+ expect(options).to eq( { host: '1.2.3.4', endpoint: 'Winreg' })
201
+ expect(RubySMB::Dcerpc::Request).to have_received(:new).with({ opnum: stub_packet.opnum }, options)
202
+ end
178
203
 
179
- before :example do
180
- allow(RubySMB::Dcerpc::Bind).to receive(:new).and_return(bind_packet)
181
- allow(pipe).to receive(:write)
182
- allow(pipe).to receive(:read)
183
- bind_ack_packet.p_result_list.n_results = 1
184
- bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::ACCEPTANCE
185
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(bind_ack_packet)
186
- end
204
+ it 'sets DCERPC request stub to the stub packet passed as argument' do
205
+ pipe.dcerpc_request(stub_packet, options)
206
+ expect(request_stub).to have_received(:read).with(stub_packet.to_binary_s)
207
+ end
187
208
 
188
- it 'creates a Bind packet' do
189
- expect(RubySMB::Dcerpc::Bind).to receive(:new).with(options).and_return(bind_packet)
190
- pipe.bind(options)
191
- end
209
+ it 'creates a Trans TransactNmpipeRequest packet' do
210
+ pipe.dcerpc_request(stub_packet, options)
211
+ expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to have_received(:new).with(options)
212
+ end
192
213
 
193
- it 'writes to the named pipe' do
194
- expect(pipe).to receive(:write).with(data: bind_packet.to_binary_s)
195
- pipe.bind(options)
196
- end
214
+ it 'calls Tree #set_header_fields' do
215
+ pipe.dcerpc_request(stub_packet, options)
216
+ expect(tree).to have_received(:set_header_fields).with(trans_nmpipe_request)
217
+ end
218
+
219
+ it 'calls TransactNmpipeRequest #set_fid' do
220
+ pipe.dcerpc_request(stub_packet, options)
221
+ expect(trans_nmpipe_request).to have_received(:set_fid).with(pipe.fid)
222
+ end
223
+
224
+ it 'sets the expected #write_data request property' do
225
+ pipe.dcerpc_request(stub_packet, options)
226
+ expect(trans_data).to have_received(:write_data=).with(binary_dcerpc_request)
227
+ end
197
228
 
198
- it 'reads the socket' do
199
- expect(pipe).to receive(:read)
200
- pipe.bind(options)
229
+ it 'sends the expected request' do
230
+ pipe.dcerpc_request(stub_packet, options)
231
+ expect(client).to have_received(:send_recv).with(trans_nmpipe_request)
232
+ end
233
+
234
+ it 'creates a Trans TransactNmpipeResponse packet from the response' do
235
+ pipe.dcerpc_request(stub_packet, options)
236
+ expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to have_received(:read).with(trans_nmpipe_raw_response)
237
+ end
238
+
239
+ context 'when the response is not a Trans packet' do
240
+ it 'raises an InvalidPacket exception' do
241
+ allow(trans_nmpipe_response).to receive_message_chain(:smb_header, :protocol)
242
+ allow(trans_nmpipe_response).to receive_message_chain(:smb_header, :command)
243
+ allow(trans_nmpipe_response).to receive(:valid?).and_return(false)
244
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Error::InvalidPacket)
201
245
  end
246
+ end
202
247
 
203
- it 'creates a BindAck packet from the response' do
204
- raw_response = RubySMB::Dcerpc::BindAck.new.to_binary_s
205
- allow(pipe).to receive(:read).and_return(raw_response)
206
- expect(RubySMB::Dcerpc::BindAck).to receive(:read).with(raw_response).and_return(bind_ack_packet)
207
- pipe.bind(options)
248
+ context 'when the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
249
+ it 'raises an UnexpectedStatusCode exception' do
250
+ allow(trans_nmpipe_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_INVALID_HANDLE)
251
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
208
252
  end
253
+ end
209
254
 
210
- it 'raises the expected exception when an invalid packet is received' do
211
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_raise(IOError)
212
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
255
+ context 'when the response status code is STATUS_SUCCESS' do
256
+ it 'does not raise any exception' do
257
+ expect { pipe.dcerpc_request(stub_packet, options) }.not_to raise_error
213
258
  end
214
259
 
215
- it 'raises the expected exception when it is not a BindAck packet' do
216
- response = RubySMB::Dcerpc::Bind.new
217
- allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(response)
218
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
260
+ it 'creates a DCERPC Response packet from the response' do
261
+ pipe.dcerpc_request(stub_packet, options)
262
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
219
263
  end
220
264
 
221
- it 'raises an exception when no result is returned' do
222
- bind_ack_packet.p_result_list.n_results = 0
223
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
265
+ context 'when an IOError occurs while parsing the DCERPC response' do
266
+ it 'raises an InvalidPacket exception' do
267
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
268
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
269
+ end
224
270
  end
225
271
 
226
- it 'raises an exception when result is not ACCEPTANCE' do
227
- bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::USER_REJECTION
228
- expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
272
+ context 'when the response is not a DCERPC Response packet' do
273
+ it 'raises an InvalidPacket exception' do
274
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
275
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
276
+ end
229
277
  end
230
278
 
231
- it 'returns the expected BindAck packet' do
232
- expect(pipe.bind(options)).to eq(bind_ack_packet)
279
+ it 'returns the expected stub data' do
280
+ expect(pipe.dcerpc_request(stub_packet, options)).to eq(result)
233
281
  end
234
282
  end
235
283
 
236
- describe '#request' do
237
- let(:options) { { host: '1.2.3.4' } }
238
- let(:opnum) { RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL }
239
- let(:req_packet) { RubySMB::Dcerpc::Request.new({ :opnum => opnum }, options) }
240
- let(:nmpipe_packet) { RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest.new(options) }
241
- let(:nmpipe_response) { RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse.new }
242
- let(:res_packet) { RubySMB::Dcerpc::Response.new }
243
-
284
+ context 'when the response status code is STATUS_BUFFER_OVERFLOW' do
285
+ let(:data_count) { 100 }
286
+ let(:added_raw_data) { double('Added raw data') }
244
287
  before :example do
245
- allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(req_packet)
246
- allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to receive(:new).and_return(nmpipe_packet)
247
- allow(client).to receive(:send_recv)
248
- allow(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).and_return(nmpipe_response)
249
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(res_packet)
288
+ allow(trans_nmpipe_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW)
289
+ allow(trans_nmpipe_response).to receive_message_chain(:parameter_block, :data_count => data_count)
290
+ allow(pipe).to receive(:read).and_return(added_raw_data)
291
+ allow(raw_data).to receive(:<<)
292
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 1)
293
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
250
294
  end
251
295
 
252
- it 'creates a Request packet' do
253
- expect(RubySMB::Dcerpc::Request).to receive(:new).and_return(req_packet)
254
- pipe.request(opnum, options)
296
+ it 'does not raise any exception' do
297
+ expect { pipe.dcerpc_request(stub_packet, options) }.not_to raise_error
255
298
  end
256
299
 
257
- it 'creates a Trans TransactNmpipeRequest packet' do
258
- expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeRequest).to receive(:new).and_return(nmpipe_packet)
259
- pipe.request(opnum, options)
300
+ it 'reads the expected number of bytes and concatenate it the first response raw data' do
301
+ pipe.dcerpc_request(stub_packet, options)
302
+ expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size - data_count)
303
+ expect(raw_data).to have_received(:<<).with(added_raw_data)
260
304
  end
261
305
 
262
- it 'calls Tree #set_header_fields' do
263
- expect(tree).to receive(:set_header_fields).with(nmpipe_packet)
264
- pipe.request(opnum, options)
306
+ it 'creates a DCERPC Response packet from the updated raw data' do
307
+ pipe.dcerpc_request(stub_packet, options)
308
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
265
309
  end
266
310
 
267
- it 'calls TransactNmpipeRequest #set_fid' do
268
- expect(nmpipe_packet).to receive(:set_fid).with(pipe.fid)
269
- pipe.request(opnum, options)
311
+ context 'when an IOError occurs while parsing the DCERPC response' do
312
+ it 'raises an InvalidPacket exception' do
313
+ allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
314
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
315
+ end
270
316
  end
271
317
 
272
- it 'sets the expected data on the request' do
273
- expect(client).to receive(:send_recv) do
274
- expect(nmpipe_packet.data_block.trans_data.write_data).to eq(req_packet.to_binary_s)
318
+ context 'when the response is not a DCERPC Response packet' do
319
+ it 'raises an InvalidPacket exception' do
320
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
321
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
275
322
  end
276
- pipe.request(opnum, options)
277
323
  end
278
324
 
279
- it 'sends the expected request' do
280
- expect(client).to receive(:send_recv).with(nmpipe_packet)
281
- pipe.request(opnum, options)
325
+ context 'when the response is not the first fragment' do
326
+ it 'raises an InvalidPacket exception' do
327
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 0)
328
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
329
+ end
282
330
  end
283
331
 
284
- it 'creates a Trans TransactNmpipeResponse packet from the response' do
285
- raw_response = double('Raw response')
286
- allow(client).to receive(:send_recv).and_return(raw_response)
287
- expect(RubySMB::SMB1::Packet::Trans::TransactNmpipeResponse).to receive(:read).with(raw_response).and_return(nmpipe_response)
288
- pipe.request(opnum, options)
289
- end
332
+ context 'when the response is the last fragment' do
333
+ it 'only reads the pipe once' do
334
+ pipe.dcerpc_request(stub_packet, options)
335
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).once
336
+ end
290
337
 
291
- it 'raises the expected exception when it is not a Trans packet' do
292
- nmpipe_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2
293
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Error::InvalidPacket)
338
+ it 'returns the expected stub data' do
339
+ expect(pipe.dcerpc_request(stub_packet, options)).to eq(result)
340
+ end
294
341
  end
295
342
 
296
- it 'raises the expected exception when the status code is not STATUS_SUCCESS' do
297
- nmpipe_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_INVALID_HANDLE.value
298
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
299
- end
343
+ context 'when the response is not the last fragment' do
344
+ let(:raw_data2) { double('Raw data #2') }
345
+ let(:dcerpc_response2) { double('DCERPC Response #2') }
346
+ let(:result2) { 'Result #2' }
347
+ before :example do
348
+ allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 0)
349
+ allow(pipe).to receive(:read).with(bytes: tree.client.max_buffer_size).and_return(raw_data2)
350
+ allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_return(dcerpc_response2)
351
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
352
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
353
+ allow(dcerpc_response2).to receive(:stub).and_return(result2)
354
+ end
300
355
 
301
- it 'creates a DCERPC Response packet from the response' do
302
- nmpipe_response.data_block.trans_data.read_data = "test"
303
- expect(RubySMB::Dcerpc::Response).to receive(:read).with(nmpipe_response.data_block.trans_data.read_data)
304
- pipe.request(opnum, options)
305
- end
356
+ it 'reads the expected number of bytes' do
357
+ pipe.dcerpc_request(stub_packet, options)
358
+ expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size)
359
+ end
306
360
 
307
- it 'raises the expected exception when an invalid packet is received' do
308
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
309
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
310
- end
361
+ it 'creates a DCERPC Response packet from the new raw data' do
362
+ pipe.dcerpc_request(stub_packet, options)
363
+ expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data2)
364
+ end
311
365
 
312
- it 'raises the expected exception when it is not a Response packet' do
313
- response = RubySMB::Dcerpc::Request.new
314
- allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(response)
315
- expect { pipe.request(opnum, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
316
- end
366
+ context 'when an IOError occurs while parsing the new DCERPC response' do
367
+ it 'raises an InvalidPacket exception' do
368
+ allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_raise(IOError)
369
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
370
+ end
371
+ end
317
372
 
318
- it 'returns the expected DCERPC Response' do
319
- expect(pipe.request(opnum, options)).to eq(res_packet)
373
+ context 'when the new response is not a DCERPC Response packet' do
374
+ it 'raises an InvalidPacket exception' do
375
+ allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
376
+ expect { pipe.dcerpc_request(stub_packet, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
377
+ end
378
+ end
379
+
380
+ it 'returns the expected stub data' do
381
+ expect(pipe.dcerpc_request(stub_packet, options)).to eq(result)
382
+ end
320
383
  end
321
384
  end
322
385
  end
323
-
324
386
  end