ruby_smb 1.0.3 → 2.0.1
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 +233 -22
- data/lib/ruby_smb/client/authentication.rb +70 -33
- data/lib/ruby_smb/client/echo.rb +20 -2
- data/lib/ruby_smb/client/encryption.rb +62 -0
- data/lib/ruby_smb/client/negotiation.rb +172 -24
- data/lib/ruby_smb/client/signing.rb +19 -0
- data/lib/ruby_smb/client/tree_connect.rb +24 -18
- 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 +68 -2
- data/lib/ruby_smb/generic_packet.rb +33 -4
- data/lib/ruby_smb/smb1/commands.rb +1 -1
- data/lib/ruby_smb/smb1/file.rb +66 -15
- data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/empty_packet.rb +10 -1
- data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
- data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
- data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +3 -2
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +3 -2
- data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
- data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
- data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
- data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
- data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
- data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
- data/lib/ruby_smb/smb1/packet/write_andx_request.rb +3 -6
- data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/pipe.rb +87 -6
- data/lib/ruby_smb/smb1/tree.rb +50 -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 +103 -25
- 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/close_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
- data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/error_packet.rb +15 -3
- data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -17
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +52 -5
- data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
- data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +93 -10
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +10 -22
- data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
- data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
- data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
- data/lib/ruby_smb/smb2/pipe.rb +86 -12
- data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +65 -21
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +5 -3
- data/spec/lib/ruby_smb/client_spec.rb +1612 -108
- 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/generic_packet_spec.rb +52 -4
- data/spec/lib/ruby_smb/smb1/file_spec.rb +191 -2
- data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +68 -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/packet/trans2/find_first2_response_spec.rb +11 -2
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
- data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +272 -149
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
- 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 +323 -6
- 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 +78 -0
- 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/query_directory_response_spec.rb +8 -0
- 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 -22
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +286 -149
- data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +261 -2
- metadata +191 -83
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/smb1/dcerpc.rb +0 -67
- data/lib/ruby_smb/smb2/dcerpc.rb +0 -70
- data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +0 -37
@@ -9,6 +9,7 @@ RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequest do
|
|
9
9
|
it { is_expected.to respond_to :path_offset }
|
10
10
|
it { is_expected.to respond_to :path_length }
|
11
11
|
it { is_expected.to respond_to :path }
|
12
|
+
it { is_expected.to respond_to :tree_connect_request_extension }
|
12
13
|
|
13
14
|
it 'is little endian' do
|
14
15
|
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
@@ -30,18 +31,347 @@ RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequest do
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
|
-
describe '#
|
34
|
-
|
35
|
-
|
34
|
+
describe '#structure_size' do
|
35
|
+
it 'should be a 16-bit unsigned integer' do
|
36
|
+
expect(packet.structure_size).to be_a BinData::Uint16le
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should have a default value of 9 as per the SMB2 spec' do
|
40
|
+
expect(packet.structure_size).to eq 9
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#flags' do
|
45
|
+
it 'should be a 16-bit unsigned integer' do
|
46
|
+
expect(packet.flags).to be_a BinData::Uint16le
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#path_offset' do
|
51
|
+
it 'should be a 16-bit unsigned integer' do
|
52
|
+
expect(packet.path_offset).to be_a BinData::Uint16le
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when flags is set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
|
56
|
+
it 'should be set to the offset, in bytes, of the full share path name from the beginning of the packet header' do
|
57
|
+
packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
|
58
|
+
expect(packet.path_offset).to eq(88)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when flags is not set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
|
63
|
+
it 'should be set to the offset, in bytes, of the full share path name from the beginning of the packet header' do
|
64
|
+
expect(packet.path_offset).to eq(72)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#path_length' do
|
70
|
+
let(:path) { '\\\\server\\path' }
|
71
|
+
|
72
|
+
it 'should be a 16-bit unsigned integer' do
|
73
|
+
expect(packet.path_length).to be_a BinData::Uint16le
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when flags is set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
|
77
|
+
it 'should be the length of the full share path name (unicode) in bytes' do
|
78
|
+
packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
|
79
|
+
packet.tree_connect_request_extension.path = path
|
80
|
+
expect(packet.path_length).to eq(path.length * 2)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when flags is not set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
|
85
|
+
it 'should be the length of the full share path name (unicode) in bytes' do
|
86
|
+
packet.path = path
|
87
|
+
expect(packet.path_length).to eq(path.length * 2)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#path' do
|
93
|
+
it 'should be a unicode string' do
|
94
|
+
expect(packet.path).to be_a RubySMB::Field::String16
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'exists if #flags is not set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
|
98
|
+
packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER
|
99
|
+
expect(packet.path?).to be true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#tree_connect_request_extension' do
|
104
|
+
it 'is a TreeConnectRequestExtension structure' do
|
105
|
+
expect(packet.tree_connect_request_extension).to be_a RubySMB::SMB2::Packet::TreeConnectRequestExtension
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'exists if #flags is set to SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT' do
|
109
|
+
packet.flags = described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
|
110
|
+
expect(packet.tree_connect_request_extension?).to be true
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'reads binary data as expected' do
|
115
|
+
data = described_class.new
|
116
|
+
expect(described_class.read(data.to_binary_s)).to eq(data)
|
117
|
+
data = described_class.new(flags: described_class::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT)
|
118
|
+
data.tree_connect_request_extension.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
|
119
|
+
expect(described_class.read(data.to_binary_s)).to eq(data)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
RSpec.describe RubySMB::SMB2::Packet::TreeConnectRequestExtension do
|
124
|
+
subject(:packet) { described_class.new }
|
125
|
+
|
126
|
+
it { is_expected.to respond_to :tree_connect_context_offset }
|
127
|
+
it { is_expected.to respond_to :tree_connect_context_count }
|
128
|
+
it { is_expected.to respond_to :reserved }
|
129
|
+
it { is_expected.to respond_to :path }
|
130
|
+
it { is_expected.to respond_to :tree_connect_contexts }
|
131
|
+
|
132
|
+
it 'is little endian' do
|
133
|
+
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '#tree_connect_context_offset' do
|
137
|
+
it 'is a 32-bit unsigned integer' do
|
138
|
+
expect(packet.tree_connect_context_offset).to be_a BinData::Uint32le
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'is the offset from the start of the SMB2 TREE_CONNECT request of an array of tree connect contexts' do
|
142
|
+
tc = RubySMB::SMB2::Packet::TreeConnectRequest.new(
|
143
|
+
flags: RubySMB::SMB2::Packet::TreeConnectRequest::SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
|
144
|
+
)
|
145
|
+
expect(tc.tree_connect_request_extension.tree_connect_context_offset).to eq(16)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '#tree_connect_context_count' do
|
150
|
+
it 'should be a 16-bit unsigned integer' do
|
151
|
+
expect(packet.tree_connect_context_count).to be_a BinData::Uint16le
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should be the #tree_connect_contexts size' do
|
155
|
+
packet.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
|
156
|
+
packet.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
|
157
|
+
expect(packet.tree_connect_context_count).to eq(2)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '#reserved' do
|
162
|
+
it 'should be a binary string' do
|
163
|
+
expect(packet.reserved).to be_a BinData::String
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'is 10-bytes long' do
|
167
|
+
expect(packet.reserved.length).to eq(10)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#path' do
|
172
|
+
it 'should be a unicode string' do
|
173
|
+
expect(packet.path).to be_a RubySMB::Field::String16
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '#tree_connect_contexts' do
|
178
|
+
it 'is an Array field' do
|
179
|
+
expect(packet.tree_connect_contexts).to be_a BinData::Array
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'has #tree_connect_context_count elements' do
|
183
|
+
packet.tree_connect_context_count = 3
|
184
|
+
expect(packet.tree_connect_contexts.size).to eq(3)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'reads binary data as expected' do
|
189
|
+
data = described_class.new
|
190
|
+
expect(described_class.read(data.to_binary_s)).to eq(data)
|
191
|
+
data.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
|
192
|
+
data.tree_connect_contexts << RubySMB::SMB2::Packet::TreeConnectContext.new(context_type: 1)
|
193
|
+
expect(described_class.read(data.to_binary_s)).to eq(data)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
RSpec.describe RubySMB::SMB2::Packet::TreeConnectContext do
|
198
|
+
subject(:packet) do
|
199
|
+
described_class.new(
|
200
|
+
context_type: described_class::SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID
|
201
|
+
)
|
202
|
+
end
|
203
|
+
|
204
|
+
it { is_expected.to respond_to :context_type }
|
205
|
+
it { is_expected.to respond_to :data_length }
|
206
|
+
it { is_expected.to respond_to :reserved }
|
207
|
+
it { is_expected.to respond_to :data }
|
208
|
+
|
209
|
+
it 'is little endian' do
|
210
|
+
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
211
|
+
end
|
212
|
+
|
213
|
+
describe '#context_type' do
|
214
|
+
it 'should be a 16-bit unsigned integer' do
|
215
|
+
expect(packet.context_type).to be_a BinData::Uint16le
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe '#data_length' do
|
220
|
+
it 'should be a 16-bit unsigned integer' do
|
221
|
+
expect(packet.data_length).to be_a BinData::Uint16le
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'is the length, in bytes, of the Data field' do
|
225
|
+
expect(packet.data_length).to eq(packet.data.to_binary_s.size)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe '#reserved' do
|
230
|
+
it 'is a 32-bit unsigned integer' do
|
231
|
+
expect(packet.reserved).to be_a BinData::Uint32le
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe '#data' do
|
236
|
+
it 'is a BinData Choice' do
|
237
|
+
expect(packet.data).to be_a BinData::Choice
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'contains the structure defined by #context_type' do
|
241
|
+
expect(packet.data).to eq(RubySMB::SMB2::Packet::RemotedIdentityTreeConnectContext.new)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'reads binary data as expected' do
|
246
|
+
data = described_class.new(context_type: 1)
|
247
|
+
expect(described_class.read(data.to_binary_s)).to eq(data)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
RSpec.describe RubySMB::SMB2::Packet::RemotedIdentityTreeConnectContext do
|
252
|
+
subject(:packet) { described_class.new }
|
253
|
+
|
254
|
+
it { is_expected.to respond_to :ticket_type }
|
255
|
+
it { is_expected.to respond_to :ticket_size }
|
256
|
+
it { is_expected.to respond_to :user }
|
257
|
+
it { is_expected.to respond_to :user_name }
|
258
|
+
it { is_expected.to respond_to :domain }
|
259
|
+
it { is_expected.to respond_to :groups }
|
260
|
+
it { is_expected.to respond_to :restricted_groups }
|
261
|
+
it { is_expected.to respond_to :privileges }
|
262
|
+
it { is_expected.to respond_to :primary_group }
|
263
|
+
it { is_expected.to respond_to :owner }
|
264
|
+
it { is_expected.to respond_to :default_dacl }
|
265
|
+
it { is_expected.to respond_to :device_groups }
|
266
|
+
it { is_expected.to respond_to :user_claims }
|
267
|
+
it { is_expected.to respond_to :device_claims }
|
268
|
+
it { is_expected.to respond_to :ticket_info }
|
269
|
+
|
270
|
+
it 'is little endian' do
|
271
|
+
expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
|
272
|
+
end
|
273
|
+
|
274
|
+
describe '#ticket_type' do
|
275
|
+
it 'should be a 16-bit unsigned integer' do
|
276
|
+
expect(packet.ticket_type).to be_a BinData::Uint16le
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'should be 1' do
|
280
|
+
expect(packet.ticket_type).to eq(1)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe '#ticket_size' do
|
285
|
+
it 'should be a 16-bit unsigned integer' do
|
286
|
+
expect(packet.ticket_size).to be_a BinData::Uint16le
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'is the total size of this structure' do
|
290
|
+
packet.ticket_info = 'Ticket Info'
|
291
|
+
expect(packet.ticket_size).to eq(packet.num_bytes)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe '#user' do
|
296
|
+
it 'should be a 16-bit unsigned integer' do
|
297
|
+
expect(packet.user).to be_a BinData::Uint16le
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
describe '#user_name' do
|
302
|
+
it 'should be a 16-bit unsigned integer' do
|
303
|
+
expect(packet.user_name).to be_a BinData::Uint16le
|
304
|
+
end
|
305
|
+
end
|
36
306
|
|
37
|
-
|
38
|
-
|
39
|
-
expect(packet.
|
307
|
+
describe '#domain' do
|
308
|
+
it 'should be a 16-bit unsigned integer' do
|
309
|
+
expect(packet.domain).to be_a BinData::Uint16le
|
40
310
|
end
|
311
|
+
end
|
41
312
|
|
42
|
-
|
43
|
-
|
44
|
-
expect(packet.
|
313
|
+
describe '#groups' do
|
314
|
+
it 'should be a 16-bit unsigned integer' do
|
315
|
+
expect(packet.groups).to be_a BinData::Uint16le
|
45
316
|
end
|
46
317
|
end
|
318
|
+
|
319
|
+
describe '#restricted_groups' do
|
320
|
+
it 'should be a 16-bit unsigned integer' do
|
321
|
+
expect(packet.restricted_groups).to be_a BinData::Uint16le
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe '#privileges' do
|
326
|
+
it 'should be a 16-bit unsigned integer' do
|
327
|
+
expect(packet.privileges).to be_a BinData::Uint16le
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe '#primary_group' do
|
332
|
+
it 'should be a 16-bit unsigned integer' do
|
333
|
+
expect(packet.primary_group).to be_a BinData::Uint16le
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
describe '#owner' do
|
338
|
+
it 'should be a 16-bit unsigned integer' do
|
339
|
+
expect(packet.owner).to be_a BinData::Uint16le
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
describe '#default_dacl' do
|
344
|
+
it 'should be a 16-bit unsigned integer' do
|
345
|
+
expect(packet.default_dacl).to be_a BinData::Uint16le
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
describe '#device_groups' do
|
350
|
+
it 'should be a 16-bit unsigned integer' do
|
351
|
+
expect(packet.device_groups).to be_a BinData::Uint16le
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
describe '#user_claims' do
|
356
|
+
it 'should be a 16-bit unsigned integer' do
|
357
|
+
expect(packet.user_claims).to be_a BinData::Uint16le
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
describe '#device_claims' do
|
362
|
+
it 'should be a 16-bit unsigned integer' do
|
363
|
+
expect(packet.device_claims).to be_a BinData::Uint16le
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
describe '#ticket_info' do
|
368
|
+
it 'should be string' do
|
369
|
+
expect(packet.ticket_info).to be_a BinData::String
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'reads binary data as expected' do
|
374
|
+
data = described_class.new(ticket_info: 'ticket info')
|
375
|
+
expect(described_class.read(data.to_binary_s)).to eq(data)
|
376
|
+
end
|
47
377
|
end
|
@@ -30,27 +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
|
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)
|
55
36
|
end
|
56
37
|
end
|
@@ -27,14 +27,80 @@ 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
|
+
|
37
|
+
subject(:pipe) { described_class.new(name: filename, response: create_response, tree: tree) }
|
38
|
+
|
39
|
+
describe '#peek' do
|
40
|
+
let(:request) { RubySMB::SMB2::Packet::IoctlRequest.new }
|
41
|
+
let(:raw_response) { double('Raw response') }
|
42
|
+
let(:response) { double('Response') }
|
43
|
+
|
44
|
+
before :example do
|
45
|
+
allow(RubySMB::SMB2::Packet::IoctlRequest).to receive(:new).and_return(request)
|
46
|
+
allow(client).to receive(:send_recv).and_return(raw_response)
|
47
|
+
allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).and_return(response)
|
48
|
+
allow(response).to receive(:valid?).and_return(true)
|
49
|
+
allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_SUCCESS)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'creates a IoctlRequest'do
|
53
|
+
expect(RubySMB::SMB2::Packet::IoctlRequest).to receive(:new)
|
54
|
+
pipe.peek
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'sets the request #ctl_code field' do
|
58
|
+
expect(request).to receive(:ctl_code=).with(RubySMB::Fscc::ControlCodes::FSCTL_PIPE_PEEK)
|
59
|
+
pipe.peek
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'sets the request #is_fsctl flag to true' do
|
63
|
+
pipe.peek
|
64
|
+
expect(request.flags.is_fsctl).to eq 1
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'sets the request #max_output_response field to the expected value' do
|
68
|
+
pipe.peek(peek_size: 10)
|
69
|
+
expect(request.max_output_response).to eq(16 + 10)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'calls #set_header_fields' do
|
73
|
+
expect(pipe).to receive(:set_header_fields).with(request)
|
74
|
+
pipe.peek
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'calls Client #send_recv' do
|
78
|
+
expect(client).to receive(:send_recv).with(request)
|
79
|
+
pipe.peek
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'parses the response as a SMB2 IoctlResponse packet' do
|
83
|
+
expect(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).with(raw_response)
|
84
|
+
pipe.peek
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'raises an InvalidPacket exception if the response is not valid' do
|
88
|
+
allow(response).to receive(:valid?).and_return(false)
|
89
|
+
smb2_header = double('SMB2 Header')
|
90
|
+
allow(response).to receive(:smb2_header).and_return(smb2_header)
|
91
|
+
allow(smb2_header).to receive_messages(:protocol => nil, :command => nil)
|
92
|
+
expect { pipe.peek }.to raise_error(RubySMB::Error::InvalidPacket)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'raises an UnexpectedStatusCode exception if the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
|
96
|
+
allow(response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_OBJECT_NAME_NOT_FOUND)
|
97
|
+
expect { pipe.peek }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
98
|
+
end
|
36
99
|
|
37
|
-
|
100
|
+
it 'returns the expected response' do
|
101
|
+
expect(pipe.peek).to eq(response)
|
102
|
+
end
|
103
|
+
end
|
38
104
|
|
39
105
|
describe '#peek_available' do
|
40
106
|
it 'reads the correct number of bytes available' do
|
@@ -61,206 +127,277 @@ RSpec.describe RubySMB::SMB2::Pipe do
|
|
61
127
|
end
|
62
128
|
end
|
63
129
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
allow(pipe).to receive(:bind)
|
71
|
-
allow(pipe).to receive(:request).and_return(dcerpc_response)
|
72
|
-
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)
|
73
136
|
end
|
137
|
+
end
|
74
138
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
79
150
|
|
80
|
-
|
81
|
-
|
82
|
-
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
|
83
155
|
end
|
156
|
+
end
|
84
157
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
expect(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).with(stub)
|
90
|
-
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
|
91
162
|
end
|
163
|
+
end
|
164
|
+
end
|
92
165
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
{:name=>"IPC$", :type=>"IPC", :comment=>"Remote IPC"},
|
104
|
-
{:name=>"ADMIN$", :type=>"DISK", :comment=>"Remote Admin"},
|
105
|
-
]
|
106
|
-
allow(RubySMB::Dcerpc::Srvsvc::NetShareEnumAll).to receive(:parse_response).and_return(shares)
|
107
|
-
expect(pipe.net_share_enum_all(host)).to eq(output)
|
108
|
-
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)
|
109
176
|
end
|
110
177
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
115
183
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
bind_ack_packet.p_result_list.n_results = 1
|
121
|
-
bind_ack_packet.p_result_list.p_results[0].result = RubySMB::Dcerpc::BindAck::ACCEPTANCE
|
122
|
-
allow(RubySMB::Dcerpc::BindAck).to receive(:read).and_return(bind_ack_packet)
|
123
|
-
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
|
124
188
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
129
194
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
134
254
|
|
135
|
-
|
136
|
-
|
137
|
-
|
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)
|
138
261
|
end
|
262
|
+
end
|
139
263
|
|
140
|
-
|
141
|
-
|
142
|
-
allow(
|
143
|
-
expect(
|
144
|
-
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)
|
145
268
|
end
|
269
|
+
end
|
146
270
|
|
147
|
-
|
148
|
-
|
149
|
-
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
|
150
274
|
end
|
151
275
|
|
152
|
-
it '
|
153
|
-
|
154
|
-
|
155
|
-
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)
|
156
279
|
end
|
157
280
|
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
161
286
|
end
|
162
287
|
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
166
293
|
end
|
167
294
|
|
168
|
-
it 'returns the expected
|
169
|
-
expect(pipe.
|
295
|
+
it 'returns the expected stub data' do
|
296
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
170
297
|
end
|
171
298
|
end
|
172
299
|
|
173
|
-
|
174
|
-
let(:
|
175
|
-
let(:
|
176
|
-
let(:req_packet) { RubySMB::Dcerpc::Request.new({ :opnum => opnum }, options) }
|
177
|
-
let(:ioctl_response) { RubySMB::SMB2::Packet::IoctlResponse.new }
|
178
|
-
let(:res_packet) { RubySMB::Dcerpc::Response.new }
|
179
|
-
|
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') }
|
180
303
|
before :example do
|
181
|
-
allow(
|
182
|
-
allow(
|
183
|
-
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)
|
184
310
|
end
|
185
311
|
|
186
|
-
it '
|
187
|
-
expect(
|
188
|
-
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
|
189
314
|
end
|
190
315
|
|
191
|
-
it '
|
192
|
-
|
193
|
-
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)
|
194
320
|
end
|
195
321
|
|
196
|
-
it 'creates a DCERPC Response packet from the
|
197
|
-
|
198
|
-
|
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)
|
199
325
|
end
|
200
326
|
|
201
|
-
|
202
|
-
|
203
|
-
|
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
|
204
332
|
end
|
205
333
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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
|
210
339
|
end
|
211
340
|
|
212
|
-
|
213
|
-
|
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
|
214
346
|
end
|
215
|
-
end
|
216
347
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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
|
222
353
|
|
223
|
-
|
224
|
-
|
354
|
+
it 'returns the expected stub data' do
|
355
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
356
|
+
end
|
225
357
|
end
|
226
358
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
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
|
231
371
|
|
232
|
-
|
233
|
-
|
234
|
-
expect(
|
235
|
-
expect(req.flags.is_fsctl).to eq(0x00000001)
|
236
|
-
expect(req.buffer).to eq(action.to_binary_s)
|
237
|
-
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)
|
238
375
|
end
|
239
|
-
pipe.ioctl_send_recv(action, options)
|
240
|
-
end
|
241
376
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
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
|
246
381
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
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
|
252
388
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
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
|
259
395
|
|
260
|
-
|
261
|
-
|
396
|
+
it 'returns the expected stub data' do
|
397
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
398
|
+
end
|
262
399
|
end
|
263
400
|
end
|
264
401
|
end
|
265
|
-
end
|
266
402
|
|
403
|
+
end
|