ruby_smb 1.0.5 → 2.0.3
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/anonymous_auth.rb +3 -3
- data/examples/append_file.rb +10 -8
- data/examples/authenticate.rb +9 -5
- data/examples/delete_file.rb +8 -6
- data/examples/enum_registry_key.rb +29 -0
- data/examples/enum_registry_values.rb +31 -0
- data/examples/list_directory.rb +8 -6
- data/examples/negotiate.rb +51 -8
- data/examples/negotiate_with_netbios_service.rb +9 -5
- data/examples/net_share_enum_all.rb +6 -4
- data/examples/pipes.rb +13 -13
- data/examples/query_service_status.rb +64 -0
- data/examples/read_file.rb +8 -6
- data/examples/read_file_encryption.rb +56 -0
- data/examples/read_registry_key_value.rb +33 -0
- data/examples/rename_file.rb +9 -7
- data/examples/tree_connect.rb +7 -5
- data/examples/write_file.rb +9 -7
- data/lib/ruby_smb.rb +4 -1
- data/lib/ruby_smb/client.rb +239 -21
- 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 +154 -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 +40 -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 +6 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +260 -16
- data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
- data/lib/ruby_smb/dcerpc/request.rb +41 -9
- data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +38 -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/svcctl.rb +479 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
- data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
- data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
- data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +421 -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/create_key_request.rb +73 -0
- data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -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 +40 -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/dcerpc/winreg/save_key_request.rb +37 -0
- data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
- data/lib/ruby_smb/dispatcher/base.rb +1 -1
- data/lib/ruby_smb/dispatcher/socket.rb +5 -4
- data/lib/ruby_smb/error.rb +28 -1
- data/lib/ruby_smb/field/stringz16.rb +17 -1
- data/lib/ruby_smb/nbss/session_header.rb +4 -4
- data/lib/ruby_smb/smb1/commands.rb +1 -1
- data/lib/ruby_smb/smb1/file.rb +8 -14
- 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 +81 -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 +51 -61
- 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 +2 -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 +80 -3
- data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +32 -20
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +5 -3
- data/spec/lib/ruby_smb/client_spec.rb +1583 -102
- 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 +1729 -0
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
- data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +135 -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/svcctl/change_service_config_w_request_spec.rb +191 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
- data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -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/create_key_request_spec.rb +110 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +104 -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 +95 -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 +35 -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 +138 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +884 -0
- data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
- data/spec/lib/ruby_smb/error_spec.rb +59 -0
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
- data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
- data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
- 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 +216 -147
- 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 +146 -68
- 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 +3 -24
- 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 +226 -148
- data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +88 -9
- metadata +257 -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,284 @@ 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
|
158
164
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
["IPC$", "IPC", "Remote IPC"],
|
164
|
-
["ADMIN$", "DISK", "Remote Admin"]
|
165
|
-
]
|
166
|
-
output = [
|
167
|
-
{:name=>"C$", :type=>"DISK", :comment=>"Default share"},
|
168
|
-
{:name=>"Shared", :type=>"DISK", :comment=>""},
|
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)
|
165
|
+
context 'with \'svcctl\' filename' do
|
166
|
+
it 'extends svcctl class' do
|
167
|
+
pipe = described_class.new(tree: tree, response: create_response, name: 'svcctl')
|
168
|
+
expect(pipe.respond_to?(:query_service_config)).to be true
|
174
169
|
end
|
175
170
|
end
|
171
|
+
end
|
176
172
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
173
|
+
describe '#dcerpc_request' do
|
174
|
+
let(:options) { { host: '1.2.3.4' } }
|
175
|
+
let(:stub_packet ) { RubySMB::Dcerpc::Winreg::OpenKeyRequest.new }
|
176
|
+
let(:dcerpc_request) { double('DCERPC Request') }
|
177
|
+
let(:request_stub) { double('Request stub') }
|
178
|
+
before :example do
|
179
|
+
allow(RubySMB::Dcerpc::Request).to receive(:new).and_return(dcerpc_request)
|
180
|
+
allow(dcerpc_request).to receive(:stub).and_return(request_stub)
|
181
|
+
allow(request_stub).to receive(:read)
|
182
|
+
allow(pipe).to receive(:ioctl_send_recv)
|
183
|
+
end
|
181
184
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
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
|
185
|
+
it 'creates a Request packet with the expected arguments' do
|
186
|
+
pipe.dcerpc_request(stub_packet, options)
|
187
|
+
expect(options).to eq( { host: '1.2.3.4', endpoint: 'Winreg' })
|
188
|
+
expect(RubySMB::Dcerpc::Request).to have_received(:new).with({ opnum: stub_packet.opnum }, options)
|
189
|
+
end
|
190
190
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
191
|
+
it 'sets DCERPC request stub to the stub packet passed as argument' do
|
192
|
+
pipe.dcerpc_request(stub_packet, options)
|
193
|
+
expect(request_stub).to have_received(:read).with(stub_packet.to_binary_s)
|
194
|
+
end
|
195
195
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
196
|
+
it 'calls #ioctl_send_recv with the expected arguments' do
|
197
|
+
pipe.dcerpc_request(stub_packet, options)
|
198
|
+
expect(pipe).to have_received(:ioctl_send_recv).with(dcerpc_request, options)
|
199
|
+
end
|
200
|
+
end
|
200
201
|
|
201
|
-
|
202
|
-
|
203
|
-
|
202
|
+
describe '#ioctl_send_recv' do
|
203
|
+
let(:ioctl_request_packet) { double('IoctlRequest') }
|
204
|
+
let(:flags) { double('Flags') }
|
205
|
+
let(:dcerpc_request) { double('DCERPC Request') }
|
206
|
+
let(:binary_dcerpc_request) { double('Binary DCERPC Request') }
|
207
|
+
let(:options) { { host: '1.2.3.4' } }
|
208
|
+
let(:ioctl_raw_response) { double('IOCTL raw response') }
|
209
|
+
let(:ioctl_response) { double('IOCTL response') }
|
210
|
+
let(:raw_data) { double('Raw data') }
|
211
|
+
let(:dcerpc_response) { double('DCERPC Response') }
|
212
|
+
let(:result) { 'Result' }
|
213
|
+
before :example do
|
214
|
+
allow(RubySMB::SMB2::Packet::IoctlRequest).to receive(:new).and_return(ioctl_request_packet)
|
215
|
+
allow(pipe).to receive(:set_header_fields).and_return(ioctl_request_packet)
|
216
|
+
allow(ioctl_request_packet).to receive_messages(
|
217
|
+
:ctl_code= => nil,
|
218
|
+
:flags => flags,
|
219
|
+
:buffer= => nil
|
220
|
+
)
|
221
|
+
allow(flags).to receive(:is_fsctl=)
|
222
|
+
allow(dcerpc_request).to receive(:to_binary_s).and_return(binary_dcerpc_request)
|
223
|
+
allow(client).to receive(:send_recv).and_return(ioctl_raw_response)
|
224
|
+
allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).and_return(ioctl_response)
|
225
|
+
allow(ioctl_response).to receive_messages(
|
226
|
+
:valid? => true,
|
227
|
+
:status_code => WindowsError::NTStatus::STATUS_SUCCESS,
|
228
|
+
:output_data => raw_data
|
229
|
+
)
|
230
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).and_return(dcerpc_response)
|
231
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
|
232
|
+
allow(dcerpc_response).to receive(:stub).and_return(result)
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'creates an IoctlRequest packet' do
|
236
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
237
|
+
expect(RubySMB::SMB2::Packet::IoctlRequest).to have_received(:new).with(options)
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'calls #set_header_fields' do
|
241
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
242
|
+
expect(pipe).to have_received(:set_header_fields).with(ioctl_request_packet)
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'sets the expected properties on the request packet' do
|
246
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
247
|
+
expect(ioctl_request_packet).to have_received(:ctl_code=).with(0x11C017)
|
248
|
+
expect(flags).to have_received(:is_fsctl=).with(0x1)
|
249
|
+
expect(ioctl_request_packet).to have_received(:buffer=).with(binary_dcerpc_request)
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'sends the expected request' do
|
253
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
254
|
+
expect(client).to have_received(:send_recv).with(ioctl_request_packet)
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'creates an IoctlResponse packet from the response' do
|
258
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
259
|
+
expect(RubySMB::SMB2::Packet::IoctlResponse).to have_received(:read).with(ioctl_raw_response)
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'when the response is not an IoctlResponse packet' do
|
263
|
+
it 'raises an InvalidPacket exception' do
|
264
|
+
allow(ioctl_response).to receive_message_chain(:smb2_header, :protocol)
|
265
|
+
allow(ioctl_response).to receive_message_chain(:smb2_header, :command)
|
266
|
+
allow(ioctl_response).to receive(:valid?).and_return(false)
|
267
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Error::InvalidPacket)
|
204
268
|
end
|
269
|
+
end
|
205
270
|
|
206
|
-
|
207
|
-
|
208
|
-
allow(
|
209
|
-
expect(
|
210
|
-
pipe.bind(options)
|
271
|
+
context 'when the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
|
272
|
+
it 'raises an UnexpectedStatusCode exception' do
|
273
|
+
allow(ioctl_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_INVALID_HANDLE)
|
274
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
211
275
|
end
|
276
|
+
end
|
212
277
|
|
213
|
-
|
214
|
-
|
215
|
-
expect { pipe.
|
278
|
+
context 'when the response status code is STATUS_SUCCESS' do
|
279
|
+
it 'does not raise any exception' do
|
280
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options)}.not_to raise_error
|
216
281
|
end
|
217
282
|
|
218
|
-
it '
|
219
|
-
|
220
|
-
|
221
|
-
expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
|
283
|
+
it 'creates a DCERPC Response packet from the response' do
|
284
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
285
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
|
222
286
|
end
|
223
287
|
|
224
|
-
|
225
|
-
|
226
|
-
|
288
|
+
context 'when an IOError occurs while parsing the DCERPC response' do
|
289
|
+
it 'raises an InvalidPacket exception' do
|
290
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
|
291
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
292
|
+
end
|
227
293
|
end
|
228
294
|
|
229
|
-
|
230
|
-
|
231
|
-
|
295
|
+
context 'when the response is not a DCERPC Response packet' do
|
296
|
+
it 'raises an InvalidPacket exception' do
|
297
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
|
298
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
299
|
+
end
|
232
300
|
end
|
233
301
|
|
234
|
-
it 'returns the expected
|
235
|
-
expect(pipe.
|
302
|
+
it 'returns the expected stub data' do
|
303
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
236
304
|
end
|
237
305
|
end
|
238
306
|
|
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
|
-
|
307
|
+
context 'when the response status code is STATUS_BUFFER_OVERFLOW' do
|
308
|
+
let(:data_count) { 100 }
|
309
|
+
let(:added_raw_data) { double('Added raw data') }
|
246
310
|
before :example do
|
247
|
-
allow(
|
248
|
-
allow(
|
249
|
-
allow(
|
311
|
+
allow(ioctl_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW)
|
312
|
+
allow(ioctl_response).to receive(:output_count).and_return(data_count)
|
313
|
+
allow(pipe).to receive(:read).and_return(added_raw_data)
|
314
|
+
allow(raw_data).to receive(:<<)
|
315
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 1)
|
316
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
|
250
317
|
end
|
251
318
|
|
252
|
-
it '
|
253
|
-
expect(
|
254
|
-
pipe.request(opnum, options)
|
319
|
+
it 'does not raise any exception' do
|
320
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.not_to raise_error
|
255
321
|
end
|
256
322
|
|
257
|
-
it '
|
258
|
-
|
259
|
-
pipe.
|
323
|
+
it 'reads the expected number of bytes and concatenate it the first response raw data' do
|
324
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
325
|
+
expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size - data_count)
|
326
|
+
expect(raw_data).to have_received(:<<).with(added_raw_data)
|
260
327
|
end
|
261
328
|
|
262
|
-
it 'creates a DCERPC Response packet from the
|
263
|
-
|
264
|
-
|
329
|
+
it 'creates a DCERPC Response packet from the updated raw data' do
|
330
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
331
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
|
265
332
|
end
|
266
333
|
|
267
|
-
|
268
|
-
|
269
|
-
|
334
|
+
context 'when an IOError occurs while parsing the DCERPC response' do
|
335
|
+
it 'raises an InvalidPacket exception' do
|
336
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
|
337
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
338
|
+
end
|
270
339
|
end
|
271
340
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
341
|
+
context 'when the response is not a DCERPC Response packet' do
|
342
|
+
it 'raises an InvalidPacket exception' do
|
343
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
|
344
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
345
|
+
end
|
276
346
|
end
|
277
347
|
|
278
|
-
|
279
|
-
|
348
|
+
context 'when the response is not the first fragment' do
|
349
|
+
it 'raises an InvalidPacket exception' do
|
350
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 0)
|
351
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
352
|
+
end
|
280
353
|
end
|
281
|
-
end
|
282
354
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
355
|
+
context 'when the response is the last fragment' do
|
356
|
+
it 'only reads the pipe once' do
|
357
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
358
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).once
|
359
|
+
end
|
288
360
|
|
289
|
-
|
290
|
-
|
361
|
+
it 'returns the expected stub data' do
|
362
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
363
|
+
end
|
291
364
|
end
|
292
365
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
366
|
+
context 'when the response is not the last fragment' do
|
367
|
+
let(:raw_data2) { double('Raw data #2') }
|
368
|
+
let(:dcerpc_response2) { double('DCERPC Response #2') }
|
369
|
+
let(:result2) { 'Result #2' }
|
370
|
+
before :example do
|
371
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 0)
|
372
|
+
allow(pipe).to receive(:read).with(bytes: tree.client.max_buffer_size).and_return(raw_data2)
|
373
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_return(dcerpc_response2)
|
374
|
+
allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
|
375
|
+
allow(dcerpc_response2).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
|
376
|
+
allow(dcerpc_response2).to receive(:stub).and_return(result2)
|
377
|
+
end
|
297
378
|
|
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
|
379
|
+
it 'reads the expected number of bytes' do
|
380
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
381
|
+
expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size)
|
304
382
|
end
|
305
|
-
pipe.ioctl_send_recv(action, options)
|
306
|
-
end
|
307
383
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
384
|
+
it 'creates a DCERPC Response packet from the new raw data' do
|
385
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
386
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data2)
|
387
|
+
end
|
312
388
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
389
|
+
context 'when an IOError occurs while parsing the new DCERPC response' do
|
390
|
+
it 'raises an InvalidPacket exception' do
|
391
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_raise(IOError)
|
392
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
393
|
+
end
|
394
|
+
end
|
318
395
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
396
|
+
context 'when the new response is not a DCERPC Response packet' do
|
397
|
+
it 'raises an InvalidPacket exception' do
|
398
|
+
allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
|
399
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
400
|
+
end
|
401
|
+
end
|
325
402
|
|
326
|
-
|
327
|
-
|
403
|
+
it 'returns the expected stub data' do
|
404
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
405
|
+
end
|
328
406
|
end
|
329
407
|
end
|
330
408
|
end
|
331
|
-
end
|
332
409
|
|
410
|
+
end
|