ruby_smb 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.travis.yml +4 -1
- data/README.md +35 -47
- data/examples/enum_registry_key.rb +28 -0
- data/examples/enum_registry_values.rb +30 -0
- data/examples/pipes.rb +2 -1
- data/examples/read_registry_key_value.rb +32 -0
- data/lib/ruby_smb/client/authentication.rb +43 -25
- data/lib/ruby_smb/client/echo.rb +20 -2
- data/lib/ruby_smb/client/negotiation.rb +27 -12
- data/lib/ruby_smb/client/tree_connect.rb +20 -14
- data/lib/ruby_smb/client/utils.rb +2 -2
- data/lib/ruby_smb/client/winreg.rb +46 -0
- data/lib/ruby_smb/client.rb +28 -4
- 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/net_share_enum_all.rb +9 -0
- data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -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/dcerpc/winreg.rb +340 -0
- data/lib/ruby_smb/dcerpc.rb +38 -0
- data/lib/ruby_smb/dispatcher/socket.rb +3 -1
- data/lib/ruby_smb/error.rb +40 -1
- data/lib/ruby_smb/generic_packet.rb +33 -4
- data/lib/ruby_smb/smb1/file.rb +67 -11
- 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 +2 -1
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -1
- 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 +2 -5
- data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
- data/lib/ruby_smb/smb1/pipe.rb +86 -5
- data/lib/ruby_smb/smb1/tree.rb +40 -2
- data/lib/ruby_smb/smb2/file.rb +97 -1
- 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/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 +2 -5
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +2 -1
- 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/tree_connect_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -2
- 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 +98 -11
- data/lib/ruby_smb/smb2/tree.rb +44 -6
- data/lib/ruby_smb/version.rb +1 -1
- data/lib/ruby_smb.rb +0 -1
- data/ruby_smb.gemspec +3 -3
- data/spec/lib/ruby_smb/client_spec.rb +271 -11
- 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 +3 -1
- 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/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/file_spec.rb +295 -2
- data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +78 -0
- data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +8 -0
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +322 -145
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +214 -0
- data.tar.gz.sig +0 -0
- metadata +70 -11
- 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
|
@@ -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
|
|
36
94
|
|
|
37
|
-
|
|
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
|
|
99
|
+
|
|
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,317 @@ 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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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)
|
|
176
|
+
end
|
|
177
|
+
|
|
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
|
|
183
|
+
|
|
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
|
|
188
|
+
|
|
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
|
|
194
|
+
|
|
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)
|
|
109
231
|
end
|
|
110
232
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
|
115
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
|
|
254
|
+
|
|
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)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
context 'when the response status code is STATUS_PENDING' do
|
|
265
|
+
let(:ioctl_raw_response2) { double('IOCTL raw response #2') }
|
|
266
|
+
let(:ioctl_response2) { double('IOCTL response #2') }
|
|
116
267
|
before :example do
|
|
117
|
-
allow(
|
|
118
|
-
allow(pipe).to receive(:
|
|
119
|
-
allow(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
268
|
+
allow(ioctl_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_PENDING)
|
|
269
|
+
allow(pipe).to receive(:sleep)
|
|
270
|
+
allow(dispatcher).to receive(:recv_packet).and_return(ioctl_raw_response2)
|
|
271
|
+
allow(RubySMB::SMB2::Packet::IoctlResponse).to receive(:read).with(ioctl_raw_response2).and_return(ioctl_response2)
|
|
272
|
+
allow(ioctl_response2).to receive_messages(
|
|
273
|
+
:valid? => true,
|
|
274
|
+
:output_data => raw_data,
|
|
275
|
+
:status_code => WindowsError::NTStatus::STATUS_SUCCESS
|
|
276
|
+
)
|
|
123
277
|
end
|
|
124
278
|
|
|
125
|
-
it '
|
|
126
|
-
|
|
127
|
-
pipe.
|
|
279
|
+
it 'waits 1 second' do
|
|
280
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
|
281
|
+
expect(pipe).to have_received(:sleep).with(1)
|
|
128
282
|
end
|
|
129
283
|
|
|
130
|
-
it '
|
|
131
|
-
|
|
132
|
-
|
|
284
|
+
it 'calls dispatcher #recv_packet' do
|
|
285
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
|
286
|
+
expect(dispatcher).to have_received(:recv_packet)
|
|
133
287
|
end
|
|
134
288
|
|
|
135
|
-
it '
|
|
136
|
-
|
|
137
|
-
|
|
289
|
+
it 'creates an IoctlResponse packet from the response' do
|
|
290
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
|
291
|
+
expect(RubySMB::SMB2::Packet::IoctlResponse).to have_received(:read).with(ioctl_raw_response2)
|
|
138
292
|
end
|
|
139
293
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
294
|
+
context 'when the response is not an IoctlResponse packet' do
|
|
295
|
+
it 'raises an InvalidPacket exception' do
|
|
296
|
+
allow(ioctl_response2).to receive_message_chain(:smb2_header, :protocol)
|
|
297
|
+
allow(ioctl_response2).to receive_message_chain(:smb2_header, :command)
|
|
298
|
+
allow(ioctl_response2).to receive(:valid?).and_return(false)
|
|
299
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
300
|
+
end
|
|
145
301
|
end
|
|
302
|
+
end
|
|
146
303
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
304
|
+
context 'when the response status code is not STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW' do
|
|
305
|
+
it 'raises an UnexpectedStatusCode exception' do
|
|
306
|
+
allow(ioctl_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_INVALID_HANDLE)
|
|
307
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
|
150
308
|
end
|
|
309
|
+
end
|
|
151
310
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
expect { pipe.bind(options) }.to raise_error(RubySMB::Dcerpc::Error::BindError)
|
|
311
|
+
context 'when the response status code is STATUS_SUCCESS' do
|
|
312
|
+
it 'does not raise any exception' do
|
|
313
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options)}.not_to raise_error
|
|
156
314
|
end
|
|
157
315
|
|
|
158
|
-
it '
|
|
159
|
-
|
|
160
|
-
expect
|
|
316
|
+
it 'creates a DCERPC Response packet from the response' do
|
|
317
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
|
318
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
|
|
161
319
|
end
|
|
162
320
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
321
|
+
context 'when an IOError occurs while parsing the DCERPC response' do
|
|
322
|
+
it 'raises an InvalidPacket exception' do
|
|
323
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
|
|
324
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
|
325
|
+
end
|
|
166
326
|
end
|
|
167
327
|
|
|
168
|
-
|
|
169
|
-
|
|
328
|
+
context 'when the response is not a DCERPC Response packet' do
|
|
329
|
+
it 'raises an InvalidPacket exception' do
|
|
330
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
|
|
331
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
|
332
|
+
end
|
|
170
333
|
end
|
|
171
|
-
end
|
|
172
334
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
let(:ioctl_response) { RubySMB::SMB2::Packet::IoctlResponse.new }
|
|
178
|
-
let(:res_packet) { RubySMB::Dcerpc::Response.new }
|
|
335
|
+
it 'returns the expected stub data' do
|
|
336
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
|
337
|
+
end
|
|
338
|
+
end
|
|
179
339
|
|
|
340
|
+
context 'when the response status code is STATUS_BUFFER_OVERFLOW' do
|
|
341
|
+
let(:data_count) { 100 }
|
|
342
|
+
let(:added_raw_data) { double('Added raw data') }
|
|
180
343
|
before :example do
|
|
181
|
-
allow(
|
|
182
|
-
allow(
|
|
183
|
-
allow(
|
|
344
|
+
allow(ioctl_response).to receive(:status_code).and_return(WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW)
|
|
345
|
+
allow(ioctl_response).to receive(:output_count).and_return(data_count)
|
|
346
|
+
allow(pipe).to receive(:read).and_return(added_raw_data)
|
|
347
|
+
allow(raw_data).to receive(:<<)
|
|
348
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 1)
|
|
349
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
|
|
184
350
|
end
|
|
185
351
|
|
|
186
|
-
it '
|
|
187
|
-
expect(
|
|
188
|
-
pipe.request(opnum, options)
|
|
352
|
+
it 'does not raise any exception' do
|
|
353
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.not_to raise_error
|
|
189
354
|
end
|
|
190
355
|
|
|
191
|
-
it '
|
|
192
|
-
|
|
193
|
-
pipe.
|
|
356
|
+
it 'reads the expected number of bytes and concatenate it the first response raw data' do
|
|
357
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
|
358
|
+
expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size - data_count)
|
|
359
|
+
expect(raw_data).to have_received(:<<).with(added_raw_data)
|
|
194
360
|
end
|
|
195
361
|
|
|
196
|
-
it 'creates a DCERPC Response packet from the
|
|
197
|
-
|
|
198
|
-
|
|
362
|
+
it 'creates a DCERPC Response packet from the updated raw data' do
|
|
363
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
|
364
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data)
|
|
199
365
|
end
|
|
200
366
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
367
|
+
context 'when an IOError occurs while parsing the DCERPC response' do
|
|
368
|
+
it 'raises an InvalidPacket exception' do
|
|
369
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).and_raise(IOError)
|
|
370
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
|
371
|
+
end
|
|
204
372
|
end
|
|
205
373
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
374
|
+
context 'when the response is not a DCERPC Response packet' do
|
|
375
|
+
it 'raises an InvalidPacket exception' do
|
|
376
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
|
|
377
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
|
378
|
+
end
|
|
210
379
|
end
|
|
211
380
|
|
|
212
|
-
|
|
213
|
-
|
|
381
|
+
context 'when the response is not the first fragment' do
|
|
382
|
+
it 'raises an InvalidPacket exception' do
|
|
383
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :first_frag => 0)
|
|
384
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
|
385
|
+
end
|
|
214
386
|
end
|
|
215
|
-
end
|
|
216
387
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
388
|
+
context 'when the response is the last fragment' do
|
|
389
|
+
it 'only reads the pipe once' do
|
|
390
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
|
391
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).once
|
|
392
|
+
end
|
|
222
393
|
|
|
223
|
-
|
|
224
|
-
|
|
394
|
+
it 'returns the expected stub data' do
|
|
395
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
|
396
|
+
end
|
|
225
397
|
end
|
|
226
398
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
399
|
+
context 'when the response is not the last fragment' do
|
|
400
|
+
let(:raw_data2) { double('Raw data #2') }
|
|
401
|
+
let(:dcerpc_response2) { double('DCERPC Response #2') }
|
|
402
|
+
let(:result2) { 'Result #2' }
|
|
403
|
+
before :example do
|
|
404
|
+
allow(dcerpc_response).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 0)
|
|
405
|
+
allow(pipe).to receive(:read).with(bytes: tree.client.max_buffer_size).and_return(raw_data2)
|
|
406
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_return(dcerpc_response2)
|
|
407
|
+
allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::RESPONSE)
|
|
408
|
+
allow(dcerpc_response2).to receive_message_chain(:pdu_header, :pfc_flags, :last_frag => 1)
|
|
409
|
+
allow(dcerpc_response2).to receive(:stub).and_return(result2)
|
|
410
|
+
end
|
|
231
411
|
|
|
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
|
|
412
|
+
it 'reads the expected number of bytes' do
|
|
413
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
|
414
|
+
expect(pipe).to have_received(:read).with(bytes: tree.client.max_buffer_size)
|
|
238
415
|
end
|
|
239
|
-
pipe.ioctl_send_recv(action, options)
|
|
240
|
-
end
|
|
241
416
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
417
|
+
it 'creates a DCERPC Response packet from the new raw data' do
|
|
418
|
+
pipe.ioctl_send_recv(dcerpc_request, options)
|
|
419
|
+
expect(RubySMB::Dcerpc::Response).to have_received(:read).with(raw_data2)
|
|
420
|
+
end
|
|
246
421
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
422
|
+
context 'when an IOError occurs while parsing the new DCERPC response' do
|
|
423
|
+
it 'raises an InvalidPacket exception' do
|
|
424
|
+
allow(RubySMB::Dcerpc::Response).to receive(:read).with(raw_data2).and_raise(IOError)
|
|
425
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
|
426
|
+
end
|
|
427
|
+
end
|
|
252
428
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
429
|
+
context 'when the new response is not a DCERPC Response packet' do
|
|
430
|
+
it 'raises an InvalidPacket exception' do
|
|
431
|
+
allow(dcerpc_response2).to receive_message_chain(:pdu_header, :ptype => RubySMB::Dcerpc::PTypes::FAULT)
|
|
432
|
+
expect { pipe.ioctl_send_recv(dcerpc_request, options) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
|
|
433
|
+
end
|
|
434
|
+
end
|
|
259
435
|
|
|
260
|
-
|
|
261
|
-
|
|
436
|
+
it 'returns the expected stub data' do
|
|
437
|
+
expect(pipe.ioctl_send_recv(dcerpc_request, options)).to eq(result)
|
|
438
|
+
end
|
|
262
439
|
end
|
|
263
440
|
end
|
|
264
441
|
end
|
|
265
|
-
end
|
|
266
442
|
|
|
443
|
+
end
|