ruby_smb 1.0.4 → 2.0.2
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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.travis.yml +3 -2
- data/Gemfile +6 -2
- data/README.md +35 -47
- data/examples/enum_registry_key.rb +28 -0
- data/examples/enum_registry_values.rb +30 -0
- data/examples/negotiate.rb +51 -8
- data/examples/pipes.rb +2 -1
- data/examples/read_file_encryption.rb +56 -0
- data/examples/read_registry_key_value.rb +32 -0
- data/lib/ruby_smb.rb +4 -1
- data/lib/ruby_smb/client.rb +207 -18
- data/lib/ruby_smb/client/authentication.rb +27 -8
- data/lib/ruby_smb/client/encryption.rb +62 -0
- data/lib/ruby_smb/client/negotiation.rb +153 -12
- data/lib/ruby_smb/client/signing.rb +19 -0
- data/lib/ruby_smb/client/tree_connect.rb +4 -4
- data/lib/ruby_smb/client/utils.rb +8 -7
- data/lib/ruby_smb/client/winreg.rb +46 -0
- data/lib/ruby_smb/crypto.rb +30 -0
- data/lib/ruby_smb/dcerpc.rb +38 -0
- data/lib/ruby_smb/dcerpc/bind.rb +2 -2
- data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
- data/lib/ruby_smb/dcerpc/error.rb +3 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
- data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
- data/lib/ruby_smb/dcerpc/request.rb +28 -9
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
- data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
- data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
- data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
- data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
- data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
- data/lib/ruby_smb/dispatcher/socket.rb +4 -3
- data/lib/ruby_smb/error.rb +28 -1
- data/lib/ruby_smb/smb1/commands.rb +1 -1
- data/lib/ruby_smb/smb1/file.rb +6 -4
- data/lib/ruby_smb/smb1/packet/empty_packet.rb +4 -2
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
- data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
- data/lib/ruby_smb/smb1/pipe.rb +79 -3
- data/lib/ruby_smb/smb1/tree.rb +12 -3
- data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
- data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
- data/lib/ruby_smb/smb2/file.rb +25 -43
- data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
- data/lib/ruby_smb/smb2/packet.rb +2 -0
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
- data/lib/ruby_smb/smb2/packet/error_packet.rb +9 -4
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
- data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
- data/lib/ruby_smb/smb2/pipe.rb +77 -3
- data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +23 -17
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +5 -3
- data/spec/lib/ruby_smb/client_spec.rb +1441 -61
- data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
- data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
- data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
- data/spec/lib/ruby_smb/error_spec.rb +59 -0
- data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
- data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +10 -0
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +210 -148
- data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
- data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +86 -62
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
- data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
- data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +29 -2
- data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
- data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
- data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +220 -149
- data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +53 -8
- metadata +187 -81
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
- data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -30,35 +30,8 @@ RSpec.describe RubySMB::SMB2::Packet::TreeConnectResponse do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
expect(packet.is_directory?).to be true
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'returns false if #share_type is not 0x01' do
|
40
|
-
packet.share_type = 0x02
|
41
|
-
expect(packet.is_directory?).to be false
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe '#access_rights' do
|
46
|
-
it 'is a DirectoryAccessMask if the Tree is a directory' do
|
47
|
-
allow(packet).to receive(:is_directory?).and_return(true)
|
48
|
-
expect(packet.access_rights).to be_a RubySMB::SMB2::BitField::DirectoryAccessMask
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'is a FileAccessMask if the Tree is not a directory' do
|
52
|
-
allow(packet).to receive(:is_directory?).and_return(false)
|
53
|
-
expect(packet.access_rights).to be_a RubySMB::SMB2::BitField::FileAccessMask
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'when it is not a valid FileAccessMask' do
|
57
|
-
it 'raises an InvalidBitField exception' do
|
58
|
-
allow(packet).to receive(:is_directory?).and_return(false)
|
59
|
-
allow(RubySMB::SMB2::BitField::FileAccessMask).to receive(:read).and_raise(IOError)
|
60
|
-
expect { packet.access_rights }.to raise_error(RubySMB::Error::InvalidBitField)
|
61
|
-
end
|
62
|
-
end
|
33
|
+
it 'reads binary data as expected' do
|
34
|
+
data = described_class.new
|
35
|
+
expect(described_class.read(data.to_binary_s)).to eq(data)
|
63
36
|
end
|
64
37
|
end
|
@@ -27,14 +27,14 @@ RSpec.describe RubySMB::SMB2::Pipe do
|
|
27
27
|
last_write: time
|
28
28
|
)
|
29
29
|
}
|
30
|
-
|
31
30
|
let(:ioctl_response) {
|
32
31
|
packet = RubySMB::SMB2::Packet::IoctlResponse.new
|
33
32
|
packet.buffer = "\x03\x00\x00\x00" + "\x10\x20\x30\x40" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00"
|
34
33
|
packet
|
35
34
|
}
|
35
|
+
let(:filename) { 'msf-pipe' }
|
36
36
|
|
37
|
-
subject(:pipe) { described_class.new(name:
|
37
|
+
subject(:pipe) { described_class.new(name: filename, response: create_response, tree: tree) }
|
38
38
|
|
39
39
|
describe '#peek' do
|
40
40
|
let(:request) { RubySMB::SMB2::Packet::IoctlRequest.new }
|
@@ -127,206 +127,277 @@ RSpec.describe RubySMB::SMB2::Pipe do
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
allow(pipe).to receive(:bind)
|
137
|
-
allow(pipe).to receive(:request).and_return(dcerpc_response)
|
138
|
-
allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return([])
|
130
|
+
describe '#initialize' do
|
131
|
+
context 'when name is not provided' do
|
132
|
+
it 'raises an ArgumentError' do
|
133
|
+
expect {
|
134
|
+
described_class.new(tree: tree, response: create_response, name: nil)
|
135
|
+
}.to raise_error(ArgumentError)
|
139
136
|
end
|
137
|
+
end
|
140
138
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
139
|
+
it 'calls the superclass with the expected arguments' do
|
140
|
+
expect(pipe.tree).to eq(tree)
|
141
|
+
expect(pipe.name).to eq(filename)
|
142
|
+
expect(pipe.attributes).to eq(create_response.file_attributes)
|
143
|
+
expect(pipe.guid).to eq(create_response.file_id)
|
144
|
+
expect(pipe.last_access).to eq(create_response.last_access.to_datetime)
|
145
|
+
expect(pipe.last_change).to eq(create_response.last_change.to_datetime)
|
146
|
+
expect(pipe.last_write).to eq(create_response.last_write.to_datetime)
|
147
|
+
expect(pipe.size).to eq(create_response.end_of_file)
|
148
|
+
expect(pipe.size_on_disk).to eq(create_response.allocation_size)
|
149
|
+
end
|
145
150
|
|
146
|
-
|
147
|
-
|
148
|
-
pipe.
|
151
|
+
context 'with \'srvsvc\' filename' do
|
152
|
+
it 'extends Srvsvc class' do
|
153
|
+
pipe = described_class.new(tree: tree, response: create_response, name: 'srvsvc')
|
154
|
+
expect(pipe.respond_to?(:net_share_enum_all)).to be true
|
149
155
|
end
|
156
|
+
end
|
150
157
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
expect(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).with(stub)
|
156
|
-
pipe.net_share_enum_all(host)
|
158
|
+
context 'with \'winreg\' filename' do
|
159
|
+
it 'extends Winreg class' do
|
160
|
+
pipe = described_class.new(tree: tree, response: create_response, name: 'winreg')
|
161
|
+
expect(pipe.respond_to?(:has_registry_key?)).to be true
|
157
162
|
end
|
163
|
+
end
|
164
|
+
end
|
158
165
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
{:name=>"IPC$", :type=>"IPC", :comment=>"Remote IPC"},
|
170
|
-
{:name=>"ADMIN$", :type=>"DISK", :comment=>"Remote Admin"},
|
171
|
-
]
|
172
|
-
allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return(shares)
|
173
|
-
expect(pipe.net_share_enum_all(host)).to eq(output)
|
174
|
-
end
|
166
|
+
describe '#dcerpc_request' do
|
167
|
+
let(:options) { { host: '1.2.3.4' } }
|
168
|
+
let(:stub_packet ) { RubySMB::Dcerpc::Winreg::OpenKeyRequest.new }
|
169
|
+
let(:dcerpc_request) { double('DCERPC Request') }
|
170
|
+
let(:request_stub) { double('Request stub') }
|
171
|
+
before :example do
|
172
|
+
allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(dcerpc_request)
|
173
|
+
allow(dcerpc_request).to receive(:stub).and_return(request_stub)
|
174
|
+
allow(request_stub).to receive(:read)
|
175
|
+
allow(pipe).to receive(:ioctl_send_recv)
|
175
176
|
end
|
176
177
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
178
|
+
it 'creates a Request packet with the expected arguments' do
|
179
|
+
pipe.dcerpc_request(stub_packet, options)
|
180
|
+
expect(options).to eq( { host: '1.2.3.4', endpoint: 'Winreg' })
|
181
|
+
expect(RubySMB::Dcerpc::Request).to have_received(:new).with({ opnum: stub_packet.opnum }, options)
|
182
|
+
end
|
181
183
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
bind_ack_packet.p_result_list.n_results = 1
|
187
|
-
bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::ACCEPTANCE
|
188
|
-
allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(bind_ack_packet)
|
189
|
-
end
|
184
|
+
it 'sets DCERPC request stub to the stub packet passed as argument' do
|
185
|
+
pipe.dcerpc_request(stub_packet, options)
|
186
|
+
expect(request_stub).to have_received(:read).with(stub_packet.to_binary_s)
|
187
|
+
end
|
190
188
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
189
|
+
it 'calls #ioctl_send_recv with the expected arguments' do
|
190
|
+
pipe.dcerpc_request(stub_packet, options)
|
191
|
+
expect(pipe).to have_received(:ioctl_send_recv).with(dcerpc_request, options)
|
192
|
+
end
|
193
|
+
end
|
195
194
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
195
|
+
describe '#ioctl_send_recv' do
|
196
|
+
let(:ioctl_request_packet) { double('IoctlRequest') }
|
197
|
+
let(:flags) { double('Flags') }
|
198
|
+
let(:dcerpc_request) { double('DCERPC Request') }
|
199
|
+
let(:binary_dcerpc_request) { double('Binary DCERPC Request') }
|
200
|
+
let(:options) { { host: '1.2.3.4' } }
|
201
|
+
let(:ioctl_raw_response) { double('IOCTL raw response') }
|
202
|
+
let(:ioctl_response) { double('IOCTL response') }
|
203
|
+
let(:raw_data) { double('Raw data') }
|
204
|
+
let(:dcerpc_response) { double('DCERPC Response') }
|
205
|
+
let(:result) { 'Result' }
|
206
|
+
before :example do
|
207
|
+
allow(RubySMB::SMB2::Packet::IoctlRequest).to receive(:new).and_return(ioctl_request_packet)
|
208
|
+
allow(pipe).to receive(:set_header_fields).and_return(ioctl_request_packet)
|
209
|
+
allow(ioctl_request_packet).to receive_messages(
|
210
|
+
:ctl_code= => nil,
|
211
|
+
:flags => flags,
|
212
|
+
:buffer= => nil
|
213
|
+
)
|
214
|
+
allow(flags).to receive(:is_fsctl=)
|
215
|
+
allow(dcerpc_request).to receive(:to_binary_s).and_return(binary_dcerpc_request)
|
216
|
+
allow(client).to receive(:send_recv).and_return(ioctl_raw_response)
|
217
|
+
allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).and_return(ioctl_response)
|
218
|
+
allow(ioctl_response).to receive_messages(
|
219
|
+
:valid? => true,
|
220
|
+
:status_code => WindowsError::NTStatus::STATUS_SUCCESS,
|
221
|
+
:output_data => raw_data
|
222
|
+
)
|
223
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(dcerpc_response)
|
224
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
|
225
|
+
allow(dcerpc_response).to receive(:stub).and_return(result)
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'creates an IoctlRequest packet' do
|
229
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
230
|
+
expect(RubySMB::SMB2::Packet::IoctlRequest).to have_received(:new).with(options)
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'calls #set_header_fields' do
|
234
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
235
|
+
expect(pipe).to have_received(:set_header_fields).with(ioctl_request_packet)
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'sets the expected properties on the request packet' do
|
239
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
240
|
+
expect(ioctl_request_packet).to have_received(:ctl_code=).with(0x11C017)
|
241
|
+
expect(flags).to have_received(:is_fsctl=).with(0x1)
|
242
|
+
expect(ioctl_request_packet).to have_received(:buffer=).with(binary_dcerpc_request)
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'sends the expected request' do
|
246
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
247
|
+
expect(client).to have_received(:send_recv).with(ioctl_request_packet)
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'creates an IoctlResponse packet from the response' do
|
251
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
252
|
+
expect(RubySMB::SMB2::Packet::IoctlResponse).to have_received(:read).with(ioctl_raw_response)
|
253
|
+
end
|
200
254
|
|
201
|
-
|
202
|
-
|
203
|
-
|
255
|
+
context 'when the response is not an IoctlResponse packet' do
|
256
|
+
it 'raises an InvalidPacket exception' do
|
257
|
+
allow(ioctl_response).to receive_message_chain(:smb2_header, :protocol)
|
258
|
+
allow(ioctl_response).to receive_message_chain(:smb2_header, :command)
|
259
|
+
allow(ioctl_response).to receive(:valid?).and_return(false)
|
260
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Error::InvalidPacket)
|
204
261
|
end
|
262
|
+
end
|
205
263
|
|
206
|
-
|
207
|
-
|
208
|
-
allow(
|
209
|
-
expect(
|
210
|
-
pipe.bind(options)
|
264
|
+
context 'when the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
|
265
|
+
it 'raises an UnexpectedStatusCode exception' do
|
266
|
+
allow(ioctl_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_INVALID_HANDLE)
|
267
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
211
268
|
end
|
269
|
+
end
|
212
270
|
|
213
|
-
|
214
|
-
|
215
|
-
expect { pipe.
|
271
|
+
context 'when the response status code is STATUS_SUCCESS' do
|
272
|
+
it 'does not raise any exception' do
|
273
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options)}.not_to raise_error
|
216
274
|
end
|
217
275
|
|
218
|
-
it '
|
219
|
-
|
220
|
-
|
221
|
-
expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
|
276
|
+
it 'creates a DCERPC Response packet from the response' do
|
277
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
278
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
|
222
279
|
end
|
223
280
|
|
224
|
-
|
225
|
-
|
226
|
-
|
281
|
+
context 'when an IOError occurs while parsing the DCERPC response' do
|
282
|
+
it 'raises an InvalidPacket exception' do
|
283
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
|
284
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
285
|
+
end
|
227
286
|
end
|
228
287
|
|
229
|
-
|
230
|
-
|
231
|
-
|
288
|
+
context 'when the response is not a DCERPC Response packet' do
|
289
|
+
it 'raises an InvalidPacket exception' do
|
290
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
|
291
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
292
|
+
end
|
232
293
|
end
|
233
294
|
|
234
|
-
it 'returns the expected
|
235
|
-
expect(pipe.
|
295
|
+
it 'returns the expected stub data' do
|
296
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
236
297
|
end
|
237
298
|
end
|
238
299
|
|
239
|
-
|
240
|
-
let(:
|
241
|
-
let(:
|
242
|
-
let(:req_packet) { RubySMB::Dcerpc::Request.new({ :opnum => opnum }, options) }
|
243
|
-
let(:ioctl_response) { RubySMB::SMB2::Packet::IoctlResponse.new }
|
244
|
-
let(:res_packet) { RubySMB::Dcerpc::Response.new }
|
245
|
-
|
300
|
+
context 'when the response status code is STATUS_BUFFER_OVERFLOW' do
|
301
|
+
let(:data_count) { 100 }
|
302
|
+
let(:added_raw_data) { double('Added raw data') }
|
246
303
|
before :example do
|
247
|
-
allow(
|
248
|
-
allow(
|
249
|
-
allow(
|
304
|
+
allow(ioctl_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW)
|
305
|
+
allow(ioctl_response).to receive(:output_count).and_return(data_count)
|
306
|
+
allow(pipe).to receive(:read).and_return(added_raw_data)
|
307
|
+
allow(raw_data).to receive(:<<)
|
308
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 1)
|
309
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
|
250
310
|
end
|
251
311
|
|
252
|
-
it '
|
253
|
-
expect(
|
254
|
-
pipe.request(opnum, options)
|
312
|
+
it 'does not raise any exception' do
|
313
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.not_to raise_error
|
255
314
|
end
|
256
315
|
|
257
|
-
it '
|
258
|
-
|
259
|
-
pipe.
|
316
|
+
it 'reads the expected number of bytes and concatenate it the first response raw data' do
|
317
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
318
|
+
expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size - data_count)
|
319
|
+
expect(raw_data).to have_received(:<<).with(added_raw_data)
|
260
320
|
end
|
261
321
|
|
262
|
-
it 'creates a DCERPC Response packet from the
|
263
|
-
|
264
|
-
|
322
|
+
it 'creates a DCERPC Response packet from the updated raw data' do
|
323
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
324
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
|
265
325
|
end
|
266
326
|
|
267
|
-
|
268
|
-
|
269
|
-
|
327
|
+
context 'when an IOError occurs while parsing the DCERPC response' do
|
328
|
+
it 'raises an InvalidPacket exception' do
|
329
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
|
330
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
331
|
+
end
|
270
332
|
end
|
271
333
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
334
|
+
context 'when the response is not a DCERPC Response packet' do
|
335
|
+
it 'raises an InvalidPacket exception' do
|
336
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
|
337
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
338
|
+
end
|
276
339
|
end
|
277
340
|
|
278
|
-
|
279
|
-
|
341
|
+
context 'when the response is not the first fragment' do
|
342
|
+
it 'raises an InvalidPacket exception' do
|
343
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 0)
|
344
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
345
|
+
end
|
280
346
|
end
|
281
|
-
end
|
282
347
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
348
|
+
context 'when the response is the last fragment' do
|
349
|
+
it 'only reads the pipe once' do
|
350
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
351
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).once
|
352
|
+
end
|
288
353
|
|
289
|
-
|
290
|
-
|
354
|
+
it 'returns the expected stub data' do
|
355
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
356
|
+
end
|
291
357
|
end
|
292
358
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
359
|
+
context 'when the response is not the last fragment' do
|
360
|
+
let(:raw_data2) { double('Raw data #2') }
|
361
|
+
let(:dcerpc_response2) { double('DCERPC Response #2') }
|
362
|
+
let(:result2) { 'Result #2' }
|
363
|
+
before :example do
|
364
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 0)
|
365
|
+
allow(pipe).to receive(:read).with(bytes: tree.client.max_buffer_size).and_return(raw_data2)
|
366
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_return(dcerpc_response2)
|
367
|
+
allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
|
368
|
+
allow(dcerpc_response2).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
|
369
|
+
allow(dcerpc_response2).to receive(:stub).and_return(result2)
|
370
|
+
end
|
297
371
|
|
298
|
-
|
299
|
-
|
300
|
-
expect(
|
301
|
-
expect(req.flags.is_fsctl).to eq(0x00000001)
|
302
|
-
expect(req.buffer).to eq(action.to_binary_s)
|
303
|
-
ioctl_response.to_binary_s
|
372
|
+
it 'reads the expected number of bytes' do
|
373
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
374
|
+
expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size)
|
304
375
|
end
|
305
|
-
pipe.ioctl_send_recv(action, options)
|
306
|
-
end
|
307
376
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
377
|
+
it 'creates a DCERPC Response packet from the new raw data' do
|
378
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
379
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data2)
|
380
|
+
end
|
312
381
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
382
|
+
context 'when an IOError occurs while parsing the new DCERPC response' do
|
383
|
+
it 'raises an InvalidPacket exception' do
|
384
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_raise(IOError)
|
385
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
386
|
+
end
|
387
|
+
end
|
318
388
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
389
|
+
context 'when the new response is not a DCERPC Response packet' do
|
390
|
+
it 'raises an InvalidPacket exception' do
|
391
|
+
allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
|
392
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
393
|
+
end
|
394
|
+
end
|
325
395
|
|
326
|
-
|
327
|
-
|
396
|
+
it 'returns the expected stub data' do
|
397
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
398
|
+
end
|
328
399
|
end
|
329
400
|
end
|
330
401
|
end
|
331
|
-
end
|
332
402
|
|
403
|
+
end
|