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
data/lib/ruby_smb/smb2/pipe.rb
CHANGED
|
@@ -3,18 +3,29 @@ module RubySMB
|
|
|
3
3
|
# Represents a pipe on the Remote server that we can perform
|
|
4
4
|
# various I/O operations on.
|
|
5
5
|
class Pipe < File
|
|
6
|
-
require 'ruby_smb/
|
|
6
|
+
require 'ruby_smb/dcerpc'
|
|
7
7
|
|
|
8
|
-
include RubySMB::
|
|
8
|
+
include RubySMB::Dcerpc
|
|
9
9
|
|
|
10
10
|
STATUS_CONNECTED = 0x00000003
|
|
11
11
|
STATUS_CLOSING = 0x00000004
|
|
12
12
|
|
|
13
|
+
def initialize(tree:, response:, name:)
|
|
14
|
+
raise ArgumentError, 'No Name Provided' if name.nil?
|
|
15
|
+
case name
|
|
16
|
+
when 'srvsvc'
|
|
17
|
+
extend RubySMB::Dcerpc::Srvsvc
|
|
18
|
+
when 'winreg'
|
|
19
|
+
extend RubySMB::Dcerpc::Winreg
|
|
20
|
+
end
|
|
21
|
+
super(tree: tree, response: response, name: name)
|
|
22
|
+
end
|
|
23
|
+
|
|
13
24
|
# Performs a peek operation on the named pipe
|
|
14
25
|
#
|
|
15
26
|
# @param peek_size [Integer] Amount of data to peek
|
|
16
27
|
# @return [RubySMB::SMB2::Packet::IoctlResponse]
|
|
17
|
-
# @raise [RubySMB::Error::InvalidPacket] if not a valid
|
|
28
|
+
# @raise [RubySMB::Error::InvalidPacket] if not a valid FIoctlResponse response
|
|
18
29
|
# @raise [RubySMB::Error::UnexpectedStatusCode] If status is not STATUS_BUFFER_OVERFLOW or STATUS_SUCCESS
|
|
19
30
|
def peek(peek_size: 0)
|
|
20
31
|
packet = RubySMB::SMB2::Packet::IoctlRequest.new
|
|
@@ -24,19 +35,19 @@ module RubySMB
|
|
|
24
35
|
packet.max_output_response = 16 + peek_size
|
|
25
36
|
packet = set_header_fields(packet)
|
|
26
37
|
raw_response = @tree.client.send_recv(packet)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
response = RubySMB::SMB2::Packet::IoctlResponse.read(raw_response)
|
|
39
|
+
unless response.valid?
|
|
40
|
+
raise RubySMB::Error::InvalidPacket.new(
|
|
41
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
|
42
|
+
expected_cmd: RubySMB::SMB2::Packet::IoctlResponse::COMMAND,
|
|
43
|
+
received_proto: response.smb2_header.protocol,
|
|
44
|
+
received_cmd: response.smb2_header.command
|
|
45
|
+
)
|
|
31
46
|
end
|
|
32
47
|
|
|
33
48
|
unless response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW or response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
|
34
49
|
raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
|
|
35
50
|
end
|
|
36
|
-
|
|
37
|
-
unless response.smb2_header.command == RubySMB::SMB2::Commands::IOCTL
|
|
38
|
-
raise RubySMB::Error::InvalidPacket, 'Not an IoctlResponse packet'
|
|
39
|
-
end
|
|
40
51
|
response
|
|
41
52
|
end
|
|
42
53
|
|
|
@@ -67,6 +78,82 @@ module RubySMB
|
|
|
67
78
|
state == STATUS_CONNECTED
|
|
68
79
|
end
|
|
69
80
|
|
|
81
|
+
def dcerpc_request(stub_packet, options={})
|
|
82
|
+
options.merge!(endpoint: stub_packet.class.name.split('::').at(-2))
|
|
83
|
+
dcerpc_request = RubySMB::Dcerpc::Request.new({ opnum: stub_packet.opnum }, options)
|
|
84
|
+
dcerpc_request.stub.read(stub_packet.to_binary_s)
|
|
85
|
+
ioctl_send_recv(dcerpc_request, options)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def ioctl_send_recv(action, options={})
|
|
89
|
+
request = set_header_fields(RubySMB::SMB2::Packet::IoctlRequest.new(options))
|
|
90
|
+
request.ctl_code = 0x0011C017
|
|
91
|
+
request.flags.is_fsctl = 0x00000001
|
|
92
|
+
request.buffer = action.to_binary_s
|
|
93
|
+
|
|
94
|
+
ioctl_raw_response = @tree.client.send_recv(request)
|
|
95
|
+
ioctl_response = RubySMB::SMB2::Packet::IoctlResponse.read(ioctl_raw_response)
|
|
96
|
+
unless ioctl_response.valid?
|
|
97
|
+
raise RubySMB::Error::InvalidPacket.new(
|
|
98
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
|
99
|
+
expected_cmd: RubySMB::SMB2::Packet::IoctlRequest::COMMAND,
|
|
100
|
+
received_proto: ioctl_response.smb2_header.protocol,
|
|
101
|
+
received_cmd: ioctl_response.smb2_header.command
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
# TODO: improve the handling of STATUS_PENDING responses
|
|
105
|
+
if ioctl_response.status_code == WindowsError::NTStatus::STATUS_PENDING
|
|
106
|
+
sleep 1
|
|
107
|
+
ioctl_raw_response = @tree.client.dispatcher.recv_packet
|
|
108
|
+
ioctl_response = RubySMB::SMB2::Packet::IoctlResponse.read(ioctl_raw_response)
|
|
109
|
+
unless ioctl_response.valid?
|
|
110
|
+
raise RubySMB::Error::InvalidPacket.new(
|
|
111
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
|
112
|
+
expected_cmd: RubySMB::SMB2::Packet::IoctlRequest::COMMAND,
|
|
113
|
+
received_proto: ioctl_response.smb2_header.protocol,
|
|
114
|
+
received_cmd: ioctl_response.smb2_header.command
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
elsif ![WindowsError::NTStatus::STATUS_SUCCESS,
|
|
118
|
+
WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW].include?(ioctl_response.status_code)
|
|
119
|
+
raise RubySMB::Error::UnexpectedStatusCode, ioctl_response.status_code.name
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
raw_data = ioctl_response.output_data
|
|
123
|
+
if ioctl_response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW
|
|
124
|
+
raw_data << read(bytes: @tree.client.max_buffer_size - ioctl_response.output_count)
|
|
125
|
+
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
|
126
|
+
unless dcerpc_response.pdu_header.pfc_flags.first_frag == 1
|
|
127
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, "Not the first fragment"
|
|
128
|
+
end
|
|
129
|
+
stub_data = dcerpc_response.stub.to_s
|
|
130
|
+
|
|
131
|
+
loop do
|
|
132
|
+
break if dcerpc_response.pdu_header.pfc_flags.last_frag == 1
|
|
133
|
+
raw_data = read(bytes: @tree.client.max_buffer_size)
|
|
134
|
+
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
|
135
|
+
stub_data << dcerpc_response.stub.to_s
|
|
136
|
+
end
|
|
137
|
+
stub_data
|
|
138
|
+
else
|
|
139
|
+
dcerpc_response = dcerpc_response_from_raw_response(raw_data)
|
|
140
|
+
dcerpc_response.stub.to_s
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
private
|
|
146
|
+
|
|
147
|
+
def dcerpc_response_from_raw_response(raw_data)
|
|
148
|
+
dcerpc_response = RubySMB::Dcerpc::Response.read(raw_data)
|
|
149
|
+
unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
|
|
150
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
|
|
151
|
+
end
|
|
152
|
+
dcerpc_response
|
|
153
|
+
rescue IOError
|
|
154
|
+
raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
|
|
155
|
+
end
|
|
156
|
+
|
|
70
157
|
end
|
|
71
158
|
end
|
|
72
159
|
end
|
data/lib/ruby_smb/smb2/tree.rb
CHANGED
|
@@ -34,11 +34,20 @@ module RubySMB
|
|
|
34
34
|
# Disconnects this Tree from the current session
|
|
35
35
|
#
|
|
36
36
|
# @return [WindowsError::ErrorCode] the NTStatus sent back by the server.
|
|
37
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a TreeDisconnectResponse packet
|
|
37
38
|
def disconnect!
|
|
38
39
|
request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new
|
|
39
40
|
request = set_header_fields(request)
|
|
40
41
|
raw_response = client.send_recv(request)
|
|
41
42
|
response = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(raw_response)
|
|
43
|
+
unless response.valid?
|
|
44
|
+
raise RubySMB::Error::InvalidPacket.new(
|
|
45
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
|
46
|
+
expected_cmd: RubySMB::SMB2::Packet::TreeDisconnectResponse::COMMAND,
|
|
47
|
+
received_proto: response.smb2_header.protocol,
|
|
48
|
+
received_cmd: response.smb2_header.command
|
|
49
|
+
)
|
|
50
|
+
end
|
|
42
51
|
response.status_code
|
|
43
52
|
end
|
|
44
53
|
|
|
@@ -89,9 +98,16 @@ module RubySMB
|
|
|
89
98
|
|
|
90
99
|
raw_response = client.send_recv(create_request)
|
|
91
100
|
response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
101
|
+
unless response.valid?
|
|
102
|
+
raise RubySMB::Error::InvalidPacket.new(
|
|
103
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
|
104
|
+
expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
|
|
105
|
+
received_proto: response.smb2_header.protocol,
|
|
106
|
+
received_cmd: response.smb2_header.command
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
|
110
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
|
|
95
111
|
end
|
|
96
112
|
|
|
97
113
|
case @share_type
|
|
@@ -102,7 +118,7 @@ module RubySMB
|
|
|
102
118
|
# when 0x03
|
|
103
119
|
# it's a printer!
|
|
104
120
|
else
|
|
105
|
-
raise RubySMB::Error::RubySMBError
|
|
121
|
+
raise RubySMB::Error::RubySMBError, 'Unsupported share type'
|
|
106
122
|
end
|
|
107
123
|
end
|
|
108
124
|
|
|
@@ -116,6 +132,7 @@ module RubySMB
|
|
|
116
132
|
# @param pattern [String] search pattern
|
|
117
133
|
# @param type [Class] file information class
|
|
118
134
|
# @return [Array] array of directory structures
|
|
135
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a QueryDirectoryResponse packet
|
|
119
136
|
def list(directory: nil, pattern: '*', type: RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation)
|
|
120
137
|
create_response = open_directory(directory: directory)
|
|
121
138
|
file_id = create_response.file_id
|
|
@@ -133,13 +150,20 @@ module RubySMB
|
|
|
133
150
|
loop do
|
|
134
151
|
response = client.send_recv(directory_request)
|
|
135
152
|
directory_response = RubySMB::SMB2::Packet::QueryDirectoryResponse.read(response)
|
|
153
|
+
unless directory_response.valid?
|
|
154
|
+
raise RubySMB::Error::InvalidPacket.new(
|
|
155
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
|
156
|
+
expected_cmd: RubySMB::SMB2::Packet::QueryDirectoryResponse::COMMAND,
|
|
157
|
+
received_proto: directory_response.smb2_header.protocol,
|
|
158
|
+
received_cmd: directory_response.smb2_header.command
|
|
159
|
+
)
|
|
160
|
+
end
|
|
136
161
|
|
|
137
162
|
status_code = directory_response.smb2_header.nt_status.to_nt_status
|
|
138
163
|
|
|
139
164
|
break if status_code == WindowsError::NTStatus::STATUS_NO_MORE_FILES
|
|
140
165
|
|
|
141
166
|
unless status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
|
142
|
-
|
|
143
167
|
raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
|
|
144
168
|
end
|
|
145
169
|
|
|
@@ -162,6 +186,7 @@ module RubySMB
|
|
|
162
186
|
# @param write [Boolean] whether to request write access
|
|
163
187
|
# @param delete [Boolean] whether to request delete access
|
|
164
188
|
# @return [RubySMB::SMB2::Packet::CreateResponse] the response packet returned from the server
|
|
189
|
+
# @raise [RubySMB::Error::InvalidPacket] if the response is not a CreateResponse packet
|
|
165
190
|
def open_directory(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
|
|
166
191
|
impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE,
|
|
167
192
|
read: true, write: false, delete: false)
|
|
@@ -169,7 +194,20 @@ module RubySMB
|
|
|
169
194
|
create_request = open_directory_packet(directory: directory, disposition: disposition,
|
|
170
195
|
impersonation: impersonation, read: read, write: write, delete: delete)
|
|
171
196
|
raw_response = client.send_recv(create_request)
|
|
172
|
-
RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
|
|
197
|
+
response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
|
|
198
|
+
unless response.valid?
|
|
199
|
+
raise RubySMB::Error::InvalidPacket.new(
|
|
200
|
+
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
|
201
|
+
expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
|
|
202
|
+
received_proto: response.smb2_header.protocol,
|
|
203
|
+
received_cmd: response.smb2_header.command
|
|
204
|
+
)
|
|
205
|
+
end
|
|
206
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
|
207
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
response
|
|
173
211
|
end
|
|
174
212
|
|
|
175
213
|
# Creates the Packet for the #open_directory method.
|
data/lib/ruby_smb/version.rb
CHANGED
data/lib/ruby_smb.rb
CHANGED
data/ruby_smb.gemspec
CHANGED
|
@@ -6,8 +6,8 @@ require 'ruby_smb/version'
|
|
|
6
6
|
Gem::Specification.new do |spec|
|
|
7
7
|
spec.name = 'ruby_smb'
|
|
8
8
|
spec.version = RubySMB::VERSION
|
|
9
|
-
spec.authors = ['David Maloney', 'James Lee', 'Dev Mohanty', 'Christophe De La Fuente']
|
|
10
|
-
spec.email = ['
|
|
9
|
+
spec.authors = ['Metasploit Hackers', 'David Maloney', 'James Lee', 'Dev Mohanty', 'Christophe De La Fuente']
|
|
10
|
+
spec.email = ['msfdev@metasploit.com']
|
|
11
11
|
spec.summary = 'A pure Ruby implementation of the SMB Protocol Family'
|
|
12
12
|
spec.description = ''
|
|
13
13
|
spec.homepage = 'https://github.com/rapid7/ruby_smb'
|
|
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
spec.platform = Gem::Platform::RUBY
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
spec.required_ruby_version = '>= 2.
|
|
29
|
+
spec.required_ruby_version = '>= 2.3.0'
|
|
30
30
|
|
|
31
31
|
spec.add_development_dependency 'bundler'
|
|
32
32
|
spec.add_development_dependency 'fivemat'
|
|
@@ -9,6 +9,7 @@ RSpec.describe RubySMB::Client do
|
|
|
9
9
|
let(:smb1_client) { described_class.new(dispatcher, smb2: false, username: username, password: password) }
|
|
10
10
|
let(:smb2_client) { described_class.new(dispatcher, smb1: false, username: username, password: password) }
|
|
11
11
|
let(:empty_packet) { RubySMB::SMB1::Packet::EmptyPacket.new }
|
|
12
|
+
let(:error_packet) { RubySMB::SMB2::Packet::ErrorPacket.new }
|
|
12
13
|
|
|
13
14
|
describe '#initialize' do
|
|
14
15
|
it 'should raise an ArgumentError without a valid dispatcher' do
|
|
@@ -153,6 +154,94 @@ RSpec.describe RubySMB::Client do
|
|
|
153
154
|
end
|
|
154
155
|
end
|
|
155
156
|
|
|
157
|
+
describe '#logoff!' do
|
|
158
|
+
context 'with SMB1' do
|
|
159
|
+
let(:raw_response) { double('Raw response') }
|
|
160
|
+
let(:logoff_response) {
|
|
161
|
+
RubySMB::SMB1::Packet::LogoffResponse.new(smb_header: {:command => RubySMB::SMB1::Commands::SMB_COM_LOGOFF} )
|
|
162
|
+
}
|
|
163
|
+
before :example do
|
|
164
|
+
allow(smb1_client).to receive(:send_recv).and_return(raw_response)
|
|
165
|
+
allow(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
|
|
166
|
+
allow(smb1_client).to receive(:wipe_state!)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it 'creates a LogoffRequest packet' do
|
|
170
|
+
expect(RubySMB::SMB1::Packet::LogoffRequest).to receive(:new).and_call_original
|
|
171
|
+
smb1_client.logoff!
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it 'calls #send_recv' do
|
|
175
|
+
expect(smb1_client).to receive(:send_recv)
|
|
176
|
+
smb1_client.logoff!
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'reads the raw response as a LogoffResponse packet' do
|
|
180
|
+
expect(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).with(raw_response)
|
|
181
|
+
smb1_client.logoff!
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'raise an InvalidPacket exception when the response is an empty packet' do
|
|
185
|
+
allow(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).and_return(RubySMB::SMB1::Packet::EmptyPacket.new)
|
|
186
|
+
expect {smb1_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it 'raise an InvalidPacket exception when the response is not valid' do
|
|
190
|
+
allow(logoff_response).to receive(:valid?).and_return(false)
|
|
191
|
+
expect {smb1_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it 'calls #wipe_state!' do
|
|
195
|
+
expect(smb1_client).to receive(:wipe_state!)
|
|
196
|
+
smb1_client.logoff!
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'returns the expected status code' do
|
|
200
|
+
logoff_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_PENDING.value
|
|
201
|
+
allow(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
|
|
202
|
+
expect(smb1_client.logoff!).to eq(WindowsError::NTStatus::STATUS_PENDING)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
context 'with SMB2' do
|
|
207
|
+
let(:raw_response) { double('Raw response') }
|
|
208
|
+
let(:logoff_response) {
|
|
209
|
+
RubySMB::SMB2::Packet::LogoffResponse.new(smb_header: {:command => RubySMB::SMB2::Commands::LOGOFF} )
|
|
210
|
+
}
|
|
211
|
+
before :example do
|
|
212
|
+
allow(smb2_client).to receive(:send_recv).and_return(raw_response)
|
|
213
|
+
allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
|
|
214
|
+
allow(smb2_client).to receive(:wipe_state!)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
it 'creates a LogoffRequest packet' do
|
|
218
|
+
expect(RubySMB::SMB2::Packet::LogoffRequest).to receive(:new).and_call_original
|
|
219
|
+
smb2_client.logoff!
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it 'calls #send_recv' do
|
|
223
|
+
expect(smb2_client).to receive(:send_recv)
|
|
224
|
+
smb2_client.logoff!
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it 'reads the raw response as a LogoffResponse packet' do
|
|
228
|
+
expect(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).with(raw_response)
|
|
229
|
+
smb2_client.logoff!
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it 'raise an InvalidPacket exception when the response is an error packet' do
|
|
233
|
+
allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(RubySMB::SMB2::Packet::ErrorPacket.new)
|
|
234
|
+
expect {smb2_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it 'raise an InvalidPacket exception when the response is not a LOGOFF command' do
|
|
238
|
+
logoff_response.smb2_header.command = RubySMB::SMB2::Commands::ECHO
|
|
239
|
+
allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
|
|
240
|
+
expect {smb2_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
156
245
|
context 'NetBIOS Session Service' do
|
|
157
246
|
describe '#session_request' do
|
|
158
247
|
let(:session_header) { RubySMB::Nbss::SessionHeader.new }
|
|
@@ -197,6 +286,11 @@ RSpec.describe RubySMB::Client do
|
|
|
197
286
|
allow(dispatcher).to receive(:recv_packet).and_return(negative_session_response.to_binary_s)
|
|
198
287
|
expect { client.session_request }.to raise_error(RubySMB::Error::NetBiosSessionService)
|
|
199
288
|
end
|
|
289
|
+
|
|
290
|
+
it 'raises an InvalidPacket exception when an error occurs while reading' do
|
|
291
|
+
allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_raise(IOError)
|
|
292
|
+
expect { client.session_request }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
293
|
+
end
|
|
200
294
|
end
|
|
201
295
|
|
|
202
296
|
describe '#session_request_packet' do
|
|
@@ -332,10 +426,15 @@ RSpec.describe RubySMB::Client do
|
|
|
332
426
|
expect(smb1_client.negotiate_response(smb1_extended_response_raw)).to eq smb1_extended_response
|
|
333
427
|
end
|
|
334
428
|
|
|
335
|
-
it 'raises an exception if the
|
|
429
|
+
it 'raises an exception if the response is not a SMB packet' do
|
|
336
430
|
expect { smb1_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
337
431
|
end
|
|
338
432
|
|
|
433
|
+
it 'raises an InvalidPacket error if the response is not a valid response' do
|
|
434
|
+
empty_packet.smb_header.command = RubySMB::SMB2::Commands::NEGOTIATE
|
|
435
|
+
expect { smb1_client.negotiate_response(empty_packet.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
436
|
+
end
|
|
437
|
+
|
|
339
438
|
it 'considers the response invalid if it is not an actual Negotiate Response' do
|
|
340
439
|
bogus_response = smb1_extended_response
|
|
341
440
|
bogus_response.smb_header.command = 0xff
|
|
@@ -357,6 +456,12 @@ RSpec.describe RubySMB::Client do
|
|
|
357
456
|
it 'raises an exception if the Response is invalid' do
|
|
358
457
|
expect { smb2_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
359
458
|
end
|
|
459
|
+
|
|
460
|
+
it 'considers the response invalid if it is not an actual Negotiate Response' do
|
|
461
|
+
bogus_response = smb2_response
|
|
462
|
+
bogus_response.smb2_header.command = RubySMB::SMB2::Commands::ECHO
|
|
463
|
+
expect { smb2_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
464
|
+
end
|
|
360
465
|
end
|
|
361
466
|
|
|
362
467
|
context 'with SMB1 and SMB2 enabled' do
|
|
@@ -624,7 +729,7 @@ RSpec.describe RubySMB::Client do
|
|
|
624
729
|
expect { smb1_client.smb1_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
|
625
730
|
end
|
|
626
731
|
|
|
627
|
-
it '
|
|
732
|
+
it 'raise an InvalidPacket exception when the response is not valid' do
|
|
628
733
|
expect { smb1_client.smb1_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
629
734
|
end
|
|
630
735
|
end
|
|
@@ -645,7 +750,7 @@ RSpec.describe RubySMB::Client do
|
|
|
645
750
|
expect(smb1_client.smb1_ntlmssp_final_packet(response.to_binary_s)).to eq response
|
|
646
751
|
end
|
|
647
752
|
|
|
648
|
-
it '
|
|
753
|
+
it 'raise an InvalidPacket exception when the response is not valid' do
|
|
649
754
|
expect { smb1_client.smb1_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
650
755
|
end
|
|
651
756
|
end
|
|
@@ -717,12 +822,7 @@ RSpec.describe RubySMB::Client do
|
|
|
717
822
|
expect(smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s)).to eq anonymous_response
|
|
718
823
|
end
|
|
719
824
|
|
|
720
|
-
it '
|
|
721
|
-
empty_packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
|
|
722
|
-
expect(smb1_client.smb1_anonymous_auth_response(empty_packet.to_binary_s)).to eq empty_packet
|
|
723
|
-
end
|
|
724
|
-
|
|
725
|
-
it 'raises an InvalidPacket error if the command is wrong' do
|
|
825
|
+
it 'raise an InvalidPacket exception when the response is not valid' do
|
|
726
826
|
anonymous_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
|
|
727
827
|
expect { smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
728
828
|
end
|
|
@@ -829,7 +929,7 @@ RSpec.describe RubySMB::Client do
|
|
|
829
929
|
expect { smb2_client.smb2_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
|
830
930
|
end
|
|
831
931
|
|
|
832
|
-
it '
|
|
932
|
+
it 'raise an InvalidPacket exception when the response is not valid' do
|
|
833
933
|
expect { smb2_client.smb2_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
834
934
|
end
|
|
835
935
|
end
|
|
@@ -888,7 +988,7 @@ RSpec.describe RubySMB::Client do
|
|
|
888
988
|
expect(smb2_client.smb2_ntlmssp_final_packet(response.to_binary_s)).to eq response
|
|
889
989
|
end
|
|
890
990
|
|
|
891
|
-
it '
|
|
991
|
+
it 'raise an InvalidPacket exception when the response is not valid' do
|
|
892
992
|
expect { smb2_client.smb2_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
893
993
|
end
|
|
894
994
|
end
|
|
@@ -1199,6 +1299,12 @@ RSpec.describe RubySMB::Client do
|
|
|
1199
1299
|
expect(smb1_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
|
|
1200
1300
|
expect(smb1_client.echo).to eq WindowsError::NTStatus::STATUS_ABANDONED
|
|
1201
1301
|
end
|
|
1302
|
+
|
|
1303
|
+
it 'raise an InvalidPacket exception when the response is not valid' do
|
|
1304
|
+
echo_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
|
|
1305
|
+
allow(smb1_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
|
|
1306
|
+
expect { smb1_client.echo }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
1307
|
+
end
|
|
1202
1308
|
end
|
|
1203
1309
|
|
|
1204
1310
|
context 'with SMB2' do
|
|
@@ -1210,8 +1316,162 @@ RSpec.describe RubySMB::Client do
|
|
|
1210
1316
|
expect(smb2_client).to receive(:send_recv).with(echo_request).and_return(echo_response.to_binary_s)
|
|
1211
1317
|
expect(smb2_client.smb2_echo).to eq echo_response
|
|
1212
1318
|
end
|
|
1319
|
+
|
|
1320
|
+
it 'raise an InvalidPacket exception when the response is not valid' do
|
|
1321
|
+
echo_response.smb2_header.command = RubySMB::SMB2::Commands::SESSION_SETUP
|
|
1322
|
+
allow(smb2_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
|
|
1323
|
+
expect { smb2_client.smb2_echo }.to raise_error(RubySMB::Error::InvalidPacket)
|
|
1324
|
+
end
|
|
1213
1325
|
end
|
|
1214
1326
|
end
|
|
1215
1327
|
|
|
1328
|
+
context 'Winreg' do
|
|
1329
|
+
describe '#connect_to_winreg' do
|
|
1330
|
+
let(:host) { '1.2.3.4' }
|
|
1331
|
+
let(:share) { "\\\\#{host}\\IPC$" }
|
|
1332
|
+
let(:ipc_tree) { double('IPC$ tree') }
|
|
1333
|
+
let(:named_pipe) { double('Named pipe') }
|
|
1334
|
+
before :example do
|
|
1335
|
+
allow(ipc_tree).to receive_messages(
|
|
1336
|
+
:share => share,
|
|
1337
|
+
:open_file => named_pipe
|
|
1338
|
+
)
|
|
1339
|
+
allow(client).to receive(:tree_connect).and_return(ipc_tree)
|
|
1340
|
+
end
|
|
1341
|
+
|
|
1342
|
+
context 'when the client is already connected to the IPC$ share' do
|
|
1343
|
+
before :example do
|
|
1344
|
+
client.tree_connects << ipc_tree
|
|
1345
|
+
allow(ipc_tree).to receive(:share).and_return(share)
|
|
1346
|
+
end
|
|
1347
|
+
|
|
1348
|
+
it 'does not connect to the already connected tree' do
|
|
1349
|
+
client.connect_to_winreg(host)
|
|
1350
|
+
expect(client).to_not have_received(:tree_connect)
|
|
1351
|
+
end
|
|
1352
|
+
end
|
|
1353
|
+
|
|
1354
|
+
it 'calls #tree_connect' do
|
|
1355
|
+
client.connect_to_winreg(host)
|
|
1356
|
+
expect(client).to have_received(:tree_connect).with(share)
|
|
1357
|
+
end
|
|
1358
|
+
|
|
1359
|
+
it 'open \'winreg\' file on the IPC$ Tree' do
|
|
1360
|
+
client.connect_to_winreg(host)
|
|
1361
|
+
expect(ipc_tree).to have_received(:open_file).with(filename: "winreg", write: true, read: true)
|
|
1362
|
+
end
|
|
1363
|
+
|
|
1364
|
+
it 'returns the expected opened named pipe' do
|
|
1365
|
+
expect(client.connect_to_winreg(host)).to eq(named_pipe)
|
|
1366
|
+
end
|
|
1367
|
+
|
|
1368
|
+
context 'when a block is given' do
|
|
1369
|
+
before :example do
|
|
1370
|
+
allow(named_pipe).to receive(:close)
|
|
1371
|
+
end
|
|
1372
|
+
|
|
1373
|
+
it 'yields the expected named_pipe' do
|
|
1374
|
+
client.connect_to_winreg(host) do |np|
|
|
1375
|
+
expect(np).to eq(named_pipe)
|
|
1376
|
+
end
|
|
1377
|
+
end
|
|
1378
|
+
|
|
1379
|
+
it 'closes the named pipe' do
|
|
1380
|
+
client.connect_to_winreg(host) { |np| }
|
|
1381
|
+
expect(named_pipe).to have_received(:close)
|
|
1382
|
+
end
|
|
1383
|
+
|
|
1384
|
+
it 'returns the block return value' do
|
|
1385
|
+
result = double('Result')
|
|
1386
|
+
expect(client.connect_to_winreg(host) { |np| result }).to eq(result)
|
|
1387
|
+
end
|
|
1388
|
+
end
|
|
1389
|
+
end
|
|
1390
|
+
|
|
1391
|
+
describe '#has_registry_key?' do
|
|
1392
|
+
let(:host) { '1.2.3.4' }
|
|
1393
|
+
let(:key) { 'HKLM\\Registry\\Key' }
|
|
1394
|
+
let(:named_pipe) { double('Named pipe') }
|
|
1395
|
+
let(:result) { double('Result') }
|
|
1396
|
+
before :example do
|
|
1397
|
+
allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
|
|
1398
|
+
allow(named_pipe).to receive(:has_registry_key?).and_return(result)
|
|
1399
|
+
end
|
|
1400
|
+
|
|
1401
|
+
it 'calls #connect_to_winreg to wrap the main logic around' do
|
|
1402
|
+
client.has_registry_key?(host, key)
|
|
1403
|
+
expect(client).to have_received(:connect_to_winreg).with(host)
|
|
1404
|
+
end
|
|
1405
|
+
|
|
1406
|
+
it 'calls Pipe #has_registry_key?' do
|
|
1407
|
+
client.has_registry_key?(host, key)
|
|
1408
|
+
expect(named_pipe).to have_received(:has_registry_key?).with(key)
|
|
1409
|
+
end
|
|
1410
|
+
end
|
|
1411
|
+
|
|
1412
|
+
describe '#read_registry_key_value' do
|
|
1413
|
+
let(:host) { '1.2.3.4' }
|
|
1414
|
+
let(:key) { 'HKLM\\Registry\\Key' }
|
|
1415
|
+
let(:value_name) { 'Value' }
|
|
1416
|
+
let(:named_pipe) { double('Named pipe') }
|
|
1417
|
+
let(:result) { double('Result') }
|
|
1418
|
+
before :example do
|
|
1419
|
+
allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
|
|
1420
|
+
allow(named_pipe).to receive(:read_registry_key_value).and_return(result)
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
it 'calls #connect_to_winreg to wrap the main logic around' do
|
|
1424
|
+
client.read_registry_key_value(host, key, value_name)
|
|
1425
|
+
expect(client).to have_received(:connect_to_winreg).with(host)
|
|
1426
|
+
end
|
|
1427
|
+
|
|
1428
|
+
it 'calls Pipe #read_registry_key_value' do
|
|
1429
|
+
client.read_registry_key_value(host, key, value_name)
|
|
1430
|
+
expect(named_pipe).to have_received(:read_registry_key_value).with(key, value_name)
|
|
1431
|
+
end
|
|
1432
|
+
end
|
|
1433
|
+
|
|
1434
|
+
describe '#enum_registry_key' do
|
|
1435
|
+
let(:host) { '1.2.3.4' }
|
|
1436
|
+
let(:key) { 'HKLM\\Registry\\Key' }
|
|
1437
|
+
let(:named_pipe) { double('Named pipe') }
|
|
1438
|
+
let(:result) { double('Result') }
|
|
1439
|
+
before :example do
|
|
1440
|
+
allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
|
|
1441
|
+
allow(named_pipe).to receive(:enum_registry_key).and_return(result)
|
|
1442
|
+
end
|
|
1443
|
+
|
|
1444
|
+
it 'calls #connect_to_winreg to wrap the main logic around' do
|
|
1445
|
+
client.enum_registry_key(host, key)
|
|
1446
|
+
expect(client).to have_received(:connect_to_winreg).with(host)
|
|
1447
|
+
end
|
|
1448
|
+
|
|
1449
|
+
it 'calls Pipe #enum_registry_key' do
|
|
1450
|
+
client.enum_registry_key(host, key)
|
|
1451
|
+
expect(named_pipe).to have_received(:enum_registry_key).with(key)
|
|
1452
|
+
end
|
|
1453
|
+
end
|
|
1454
|
+
|
|
1455
|
+
describe '#enum_registry_values' do
|
|
1456
|
+
let(:host) { '1.2.3.4' }
|
|
1457
|
+
let(:key) { 'HKLM\\Registry\\Key' }
|
|
1458
|
+
let(:named_pipe) { double('Named pipe') }
|
|
1459
|
+
let(:result) { double('Result') }
|
|
1460
|
+
before :example do
|
|
1461
|
+
allow(client).to receive(:connect_to_winreg).and_yield(named_pipe)
|
|
1462
|
+
allow(named_pipe).to receive(:enum_registry_values).and_return(result)
|
|
1463
|
+
end
|
|
1464
|
+
|
|
1465
|
+
it 'calls #connect_to_winreg to wrap the main logic around' do
|
|
1466
|
+
client.enum_registry_values(host, key)
|
|
1467
|
+
expect(client).to have_received(:connect_to_winreg).with(host)
|
|
1468
|
+
end
|
|
1469
|
+
|
|
1470
|
+
it 'calls Pipe #enum_registry_values' do
|
|
1471
|
+
client.enum_registry_values(host, key)
|
|
1472
|
+
expect(named_pipe).to have_received(:enum_registry_values).with(key)
|
|
1473
|
+
end
|
|
1474
|
+
end
|
|
1475
|
+
end
|
|
1216
1476
|
end
|
|
1217
1477
|
|
|
@@ -31,7 +31,7 @@ RSpec.describe RubySMB::Dcerpc::BindAck do
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
it 'should have a default value of 0xFFFF' do
|
|
34
|
-
expect(packet.max_xmit_frag).to eq
|
|
34
|
+
expect(packet.max_xmit_frag).to eq RubySMB::Dcerpc::MAX_XMIT_FRAG
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -41,7 +41,7 @@ RSpec.describe RubySMB::Dcerpc::BindAck do
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
it 'should have a default value of 0xFFFF' do
|
|
44
|
-
expect(packet.max_recv_frag).to eq
|
|
44
|
+
expect(packet.max_recv_frag).to eq RubySMB::Dcerpc::MAX_RECV_FRAG
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|