ruby_smb 1.0.4 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.travis.yml +3 -2
- data/Gemfile +6 -2
- data/README.md +35 -47
- data/examples/enum_registry_key.rb +28 -0
- data/examples/enum_registry_values.rb +30 -0
- data/examples/negotiate.rb +51 -8
- data/examples/pipes.rb +2 -1
- data/examples/read_file_encryption.rb +56 -0
- data/examples/read_registry_key_value.rb +32 -0
- data/lib/ruby_smb.rb +4 -1
- data/lib/ruby_smb/client.rb +207 -18
- data/lib/ruby_smb/client/authentication.rb +27 -8
- data/lib/ruby_smb/client/encryption.rb +62 -0
- data/lib/ruby_smb/client/negotiation.rb +153 -12
- data/lib/ruby_smb/client/signing.rb +19 -0
- data/lib/ruby_smb/client/tree_connect.rb +4 -4
- data/lib/ruby_smb/client/utils.rb +8 -7
- data/lib/ruby_smb/client/winreg.rb +46 -0
- data/lib/ruby_smb/crypto.rb +30 -0
- data/lib/ruby_smb/dcerpc.rb +38 -0
- data/lib/ruby_smb/dcerpc/bind.rb +2 -2
- data/lib/ruby_smb/dcerpc/bind_ack.rb +2 -2
- data/lib/ruby_smb/dcerpc/error.rb +3 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +95 -16
- data/lib/ruby_smb/dcerpc/pdu_header.rb +1 -1
- data/lib/ruby_smb/dcerpc/request.rb +28 -9
- data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +35 -0
- data/lib/ruby_smb/dcerpc/srvsvc.rb +10 -0
- data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +9 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +340 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_request.rb +24 -0
- data/lib/ruby_smb/dcerpc/winreg/close_key_response.rb +27 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +45 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_key_response.rb +42 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +39 -0
- data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +36 -0
- data/lib/ruby_smb/dcerpc/winreg/open_key_request.rb +34 -0
- data/lib/ruby_smb/dcerpc/winreg/open_key_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +43 -0
- data/lib/ruby_smb/dcerpc/winreg/open_root_key_response.rb +35 -0
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +27 -0
- data/lib/ruby_smb/dcerpc/winreg/query_info_key_response.rb +40 -0
- data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +39 -0
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +57 -0
- data/lib/ruby_smb/dcerpc/winreg/regsam.rb +40 -0
- data/lib/ruby_smb/dispatcher/socket.rb +4 -3
- data/lib/ruby_smb/error.rb +28 -1
- data/lib/ruby_smb/smb1/commands.rb +1 -1
- data/lib/ruby_smb/smb1/file.rb +6 -4
- data/lib/ruby_smb/smb1/packet/empty_packet.rb +4 -2
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
- data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
- data/lib/ruby_smb/smb1/pipe.rb +79 -3
- data/lib/ruby_smb/smb1/tree.rb +12 -3
- data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
- data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
- data/lib/ruby_smb/smb2/file.rb +25 -43
- data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
- data/lib/ruby_smb/smb2/packet.rb +2 -0
- data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
- data/lib/ruby_smb/smb2/packet/error_packet.rb +9 -4
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
- data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
- data/lib/ruby_smb/smb2/pipe.rb +77 -3
- data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
- data/lib/ruby_smb/smb2/tree.rb +23 -17
- data/lib/ruby_smb/version.rb +1 -1
- data/ruby_smb.gemspec +5 -3
- data/spec/lib/ruby_smb/client_spec.rb +1441 -61
- data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
- data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +2 -2
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +410 -0
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +50 -7
- data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +98 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +13 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +60 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_request_spec.rb +28 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/close_key_response_spec.rb +36 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +108 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_response_spec.rb +97 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +94 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +82 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_request_spec.rb +74 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_key_response_spec.rb +35 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +90 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +39 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_response_spec.rb +113 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +88 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +150 -0
- data/spec/lib/ruby_smb/dcerpc/winreg/regsam_spec.rb +32 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +710 -0
- data/spec/lib/ruby_smb/dcerpc_spec.rb +81 -0
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +2 -2
- data/spec/lib/ruby_smb/error_spec.rb +59 -0
- data/spec/lib/ruby_smb/smb1/file_spec.rb +9 -1
- data/spec/lib/ruby_smb/smb1/packet/empty_packet_spec.rb +10 -0
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
- data/spec/lib/ruby_smb/smb1/pipe_spec.rb +210 -148
- data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
- data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +86 -62
- data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
- data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
- data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +29 -2
- data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
- data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
- data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
- data/spec/lib/ruby_smb/smb2/pipe_spec.rb +220 -149
- data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +53 -8
- metadata +187 -81
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/smb1/dcerpc.rb +0 -72
- data/lib/ruby_smb/smb2/dcerpc.rb +0 -75
@@ -4,7 +4,8 @@ module RubySMB
|
|
4
4
|
# The SessionsFlags bit-field for a {RubySMB::SMB2::Packet::SessionSetupResponse}
|
5
5
|
class SessionFlags < BinData::Record
|
6
6
|
endian :little
|
7
|
-
|
7
|
+
bit5 :reserved3, label: 'Reserved', initial_value: 0
|
8
|
+
bit1 :encrypt_data, label: 'Encrypt Data', initial_value: 0
|
8
9
|
bit1 :null, label: 'ASYNC Command', initial_value: 0
|
9
10
|
bit1 :guest, label: 'Is Guest?', initial_value: 0
|
10
11
|
resume_byte_alignment
|
@@ -8,7 +8,7 @@ module RubySMB
|
|
8
8
|
bit2 :reserved1, label: 'Reserved Space'
|
9
9
|
bit1 :vdo_caching, label: 'VDO Caching'
|
10
10
|
bit1 :auto_caching, label: 'Auto Caching'
|
11
|
-
bit2 :reserved2
|
11
|
+
bit2 :reserved2, label: 'Reserved Space'
|
12
12
|
bit1 :dfs_root, label: 'DFS Root'
|
13
13
|
bit1 :dfs, label: 'DFS'
|
14
14
|
# byte boundary
|
@@ -20,9 +20,11 @@ module RubySMB
|
|
20
20
|
bit1 :namespace_caching, label: 'Namespace Caching'
|
21
21
|
bit1 :shared_delete, label: 'Force Shared Delete'
|
22
22
|
bit1 :restrict_exclusive_opens, label: 'Restrict Exclusive Opens'
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
# byte boundary
|
24
|
+
bit5 :reserved3, label: 'Reserved Space'
|
25
|
+
bit1 :identity_remoting, label: 'Identity Remoting'
|
26
|
+
bit2 :reserved4, label: 'Reserved Space'
|
27
|
+
bit8 :reserved5, label: 'Reserved Space'
|
26
28
|
|
27
29
|
def caching_type
|
28
30
|
if vdo_caching == 1 && auto_caching.zero?
|
data/lib/ruby_smb/smb2/file.rb
CHANGED
@@ -52,7 +52,12 @@ module RubySMB
|
|
52
52
|
# @return [RubySMB::SMB2::Tree]
|
53
53
|
attr_accessor :tree
|
54
54
|
|
55
|
-
|
55
|
+
# Whether or not the share associated with this tree connect needs to be encrypted (SMB 3.x)
|
56
|
+
# @!attribute [rw] tree_connect_encrypt_data
|
57
|
+
# @return [Boolean]
|
58
|
+
attr_accessor :tree_connect_encrypt_data
|
59
|
+
|
60
|
+
def initialize(tree:, response:, name:, encrypt: false)
|
56
61
|
raise ArgumentError, 'No Tree Provided' if tree.nil?
|
57
62
|
raise ArgumentError, 'No Response Provided' if response.nil?
|
58
63
|
|
@@ -66,6 +71,7 @@ module RubySMB
|
|
66
71
|
@last_write = response.last_write.to_datetime
|
67
72
|
@size = response.end_of_file
|
68
73
|
@size_on_disk = response.allocation_size
|
74
|
+
@tree_connect_encrypt_data = encrypt
|
69
75
|
end
|
70
76
|
|
71
77
|
# Appends the supplied data to the end of the file.
|
@@ -83,7 +89,7 @@ module RubySMB
|
|
83
89
|
# @raise [RubySMB::Error::UnexpectedStatusCode] if the response NTStatus is not STATUS_SUCCESS
|
84
90
|
def close
|
85
91
|
close_request = set_header_fields(RubySMB::SMB2::Packet::CloseRequest.new)
|
86
|
-
raw_response = tree.client.send_recv(close_request)
|
92
|
+
raw_response = tree.client.send_recv(close_request, encrypt: @tree_connect_encrypt_data)
|
87
93
|
response = RubySMB::SMB2::Packet::CloseResponse.read(raw_response)
|
88
94
|
unless response.valid?
|
89
95
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -94,7 +100,7 @@ module RubySMB
|
|
94
100
|
)
|
95
101
|
end
|
96
102
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
97
|
-
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
103
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
98
104
|
end
|
99
105
|
response.status_code
|
100
106
|
end
|
@@ -116,7 +122,7 @@ module RubySMB
|
|
116
122
|
end
|
117
123
|
|
118
124
|
read_request = read_packet(read_length: atomic_read_size, offset: offset)
|
119
|
-
raw_response = tree.client.send_recv(read_request)
|
125
|
+
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
120
126
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
121
127
|
unless response.valid?
|
122
128
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -127,7 +133,7 @@ module RubySMB
|
|
127
133
|
)
|
128
134
|
end
|
129
135
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
130
|
-
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
136
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
131
137
|
end
|
132
138
|
|
133
139
|
data = response.buffer.to_binary_s
|
@@ -139,7 +145,7 @@ module RubySMB
|
|
139
145
|
atomic_read_size = remaining_bytes if remaining_bytes < tree.client.server_max_read_size
|
140
146
|
|
141
147
|
read_request = read_packet(read_length: atomic_read_size, offset: offset)
|
142
|
-
raw_response = tree.client.send_recv(read_request)
|
148
|
+
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
143
149
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
144
150
|
unless response.valid?
|
145
151
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -150,7 +156,7 @@ module RubySMB
|
|
150
156
|
)
|
151
157
|
end
|
152
158
|
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
153
|
-
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
159
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
154
160
|
end
|
155
161
|
|
156
162
|
data << response.buffer.to_binary_s
|
@@ -170,10 +176,10 @@ module RubySMB
|
|
170
176
|
read_request.offset = offset
|
171
177
|
read_request
|
172
178
|
end
|
173
|
-
|
179
|
+
|
174
180
|
def send_recv_read(read_length: 0, offset: 0)
|
175
181
|
read_request = read_packet(read_length: read_length, offset: offset)
|
176
|
-
raw_response = tree.client.send_recv(read_request)
|
182
|
+
raw_response = tree.client.send_recv(read_request, encrypt: @tree_connect_encrypt_data)
|
177
183
|
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
178
184
|
unless response.valid?
|
179
185
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -183,20 +189,8 @@ module RubySMB
|
|
183
189
|
received_cmd: response.smb2_header.command
|
184
190
|
)
|
185
191
|
end
|
186
|
-
|
187
|
-
|
188
|
-
raw_response = tree.client.dispatcher.recv_packet
|
189
|
-
response = RubySMB::SMB2::Packet::ReadResponse.read(raw_response)
|
190
|
-
unless response.valid?
|
191
|
-
raise RubySMB::Error::InvalidPacket.new(
|
192
|
-
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
193
|
-
expected_cmd: RubySMB::SMB2::Packet::ReadResponse::COMMAND,
|
194
|
-
received_proto: response.smb2_header.protocol,
|
195
|
-
received_cmd: response.smb2_header.command
|
196
|
-
)
|
197
|
-
end
|
198
|
-
elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
|
199
|
-
raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
|
192
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
193
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
200
194
|
end
|
201
195
|
response.buffer.to_binary_s
|
202
196
|
end
|
@@ -206,7 +200,7 @@ module RubySMB
|
|
206
200
|
# @return [WindowsError::ErrorCode] the NTStatus Response code
|
207
201
|
# @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
|
208
202
|
def delete
|
209
|
-
raw_response = tree.client.send_recv(delete_packet)
|
203
|
+
raw_response = tree.client.send_recv(delete_packet, encrypt: @tree_connect_encrypt_data)
|
210
204
|
response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
|
211
205
|
unless response.valid?
|
212
206
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -256,7 +250,7 @@ module RubySMB
|
|
256
250
|
|
257
251
|
while buffer.length > 0 do
|
258
252
|
write_request = write_packet(data: buffer.slice!(0,atomic_write_size), offset: offset)
|
259
|
-
raw_response = tree.client.send_recv(write_request)
|
253
|
+
raw_response = tree.client.send_recv(write_request, encrypt: @tree_connect_encrypt_data)
|
260
254
|
response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
|
261
255
|
unless response.valid?
|
262
256
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -286,10 +280,10 @@ module RubySMB
|
|
286
280
|
write_request.buffer = data
|
287
281
|
write_request
|
288
282
|
end
|
289
|
-
|
283
|
+
|
290
284
|
def send_recv_write(data:'', offset: 0)
|
291
285
|
pkt = write_packet(data: data, offset: offset)
|
292
|
-
raw_response = tree.client.send_recv(pkt)
|
286
|
+
raw_response = tree.client.send_recv(pkt, encrypt: @tree_connect_encrypt_data)
|
293
287
|
response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
|
294
288
|
unless response.valid?
|
295
289
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -299,31 +293,19 @@ module RubySMB
|
|
299
293
|
received_cmd: response.smb2_header.command
|
300
294
|
)
|
301
295
|
end
|
302
|
-
|
303
|
-
|
304
|
-
raw_response = tree.client.dispatcher.recv_packet
|
305
|
-
response = RubySMB::SMB2::Packet::WriteResponse.read(raw_response)
|
306
|
-
unless response.valid?
|
307
|
-
raise RubySMB::Error::InvalidPacket.new(
|
308
|
-
expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
|
309
|
-
expected_cmd: RubySMB::SMB2::Packet::WriteResponse::COMMAND,
|
310
|
-
received_proto: response.smb2_header.protocol,
|
311
|
-
received_cmd: response.smb2_header.command
|
312
|
-
)
|
313
|
-
end
|
314
|
-
elsif response.status_code != WindowsError::NTStatus::STATUS_SUCCESS
|
315
|
-
raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
|
296
|
+
unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
297
|
+
raise RubySMB::Error::UnexpectedStatusCode, response.status_code
|
316
298
|
end
|
317
299
|
response.write_count
|
318
300
|
end
|
319
|
-
|
301
|
+
|
320
302
|
# Rename a file
|
321
303
|
#
|
322
304
|
# @param new_file_name [String] the new name
|
323
305
|
# @return [WindowsError::ErrorCode] the NTStatus Response code
|
324
306
|
# @raise [RubySMB::Error::InvalidPacket] if the response is not a SetInfoResponse packet
|
325
307
|
def rename(new_file_name)
|
326
|
-
raw_response = tree.client.send_recv(rename_packet(new_file_name))
|
308
|
+
raw_response = tree.client.send_recv(rename_packet(new_file_name), encrypt: @tree_connect_encrypt_data)
|
327
309
|
response = RubySMB::SMB2::Packet::SetInfoResponse.read(raw_response)
|
328
310
|
unless response.valid?
|
329
311
|
raise RubySMB::Error::InvalidPacket.new(
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module SMB2
|
3
|
+
|
4
|
+
# An SMB2 PREAUTH_INTEGRITY_CAPABILITIES context struct as defined in
|
5
|
+
# [2.2.3.1.1 SMB2_PREAUTH_INTEGRITY_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5a07bd66-4734-4af8-abcf-5a44ff7ee0e5)
|
6
|
+
class PreauthIntegrityCapabilities < BinData::Record
|
7
|
+
SHA_512 = 0x0001
|
8
|
+
HASH_ALGORITM_MAP = {
|
9
|
+
SHA_512 => 'SHA512'
|
10
|
+
}
|
11
|
+
|
12
|
+
endian :little
|
13
|
+
|
14
|
+
uint16 :hash_algorithm_count, label: 'Hash Algorithm Count', initial_value: -> { hash_algorithms.size }
|
15
|
+
uint16 :salt_length, label: 'Salt Length', initial_value: -> { salt.num_bytes }
|
16
|
+
array :hash_algorithms, label: 'Hash Algorithms', type: :uint16, initial_length: -> { hash_algorithm_count }
|
17
|
+
string :salt, label: 'Salt', read_length: -> { salt_length }
|
18
|
+
end
|
19
|
+
|
20
|
+
# An SMB2 ENCRYPTION_CAPABILITIES context struct as defined in
|
21
|
+
# [2.2.3.1.2 SMB2_ENCRYPTION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/16693be7-2b27-4d3b-804b-f605bde5bcdd)
|
22
|
+
class EncryptionCapabilities < BinData::Record
|
23
|
+
AES_128_CCM = 0x0001
|
24
|
+
AES_128_GCM = 0x0002
|
25
|
+
ENCRYPTION_ALGORITHM_MAP = {
|
26
|
+
AES_128_CCM => 'AES-128-CCM',
|
27
|
+
AES_128_GCM => 'AES-128-GCM'
|
28
|
+
}
|
29
|
+
|
30
|
+
endian :little
|
31
|
+
|
32
|
+
uint16 :cipher_count, label: 'Cipher Count', initial_value: -> { ciphers.size }
|
33
|
+
array :ciphers, label: 'Ciphers', type: :uint16, initial_length: -> { cipher_count }
|
34
|
+
end
|
35
|
+
|
36
|
+
# An SMB2 COMPRESSION_CAPABILITIES context struct as defined in
|
37
|
+
# [2.2.3.1.3 SMB2_COMPRESSION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/78e0c942-ab41-472b-b117-4a95ebe88271)
|
38
|
+
class CompressionCapabilities < BinData::Record
|
39
|
+
# Flags
|
40
|
+
# Chained compression is not supported.
|
41
|
+
SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE = 0x00000000
|
42
|
+
# Chained compression is supported on this connection.
|
43
|
+
SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED = 0x00000001
|
44
|
+
|
45
|
+
# Compression Algorithms
|
46
|
+
NONE = 0x0000
|
47
|
+
LZNT1 = 0x0001
|
48
|
+
LZ77 = 0x0002
|
49
|
+
LZ77_Huffman = 0x0003
|
50
|
+
Pattern_V1 = 0x0004
|
51
|
+
COMPRESSION_ALGORITHM_MAP = {
|
52
|
+
NONE => 'NONE',
|
53
|
+
LZNT1 => 'LZNT1',
|
54
|
+
LZ77 => 'LZ77',
|
55
|
+
LZ77_Huffman => 'LZ77_Huffman',
|
56
|
+
Pattern_V1 => 'Pattern_V1'
|
57
|
+
}
|
58
|
+
|
59
|
+
endian :little
|
60
|
+
|
61
|
+
uint16 :compression_algorithm_count, label: 'Compression Algorithm Count', initial_value: -> { compression_algorithms.size }
|
62
|
+
uint16 :padding, label: 'Padding', initial_value: 0
|
63
|
+
uint32 :flags, label: 'Flags'
|
64
|
+
array :compression_algorithms, label: 'Compression Algorithms', type: :uint16, initial_length: -> { compression_algorithm_count }
|
65
|
+
end
|
66
|
+
|
67
|
+
# An SMB2 NETNAME_NEGOTIATE_CONTEXT_ID context struct as defined in
|
68
|
+
# [2.2.3.1.4 SMB2_NETNAME_NEGOTIATE_CONTEXT_ID](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ca6726bd-b9cf-43d9-b0bc-d127d3c993b3)
|
69
|
+
class NetnameNegotiateContextId < BinData::Record
|
70
|
+
endian :little
|
71
|
+
|
72
|
+
stringz16 :net_name, label: 'Net Name'
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# An SMB2 NEGOTIATE_CONTEXT struct as defined in
|
77
|
+
# [2.2.3.1 SMB2 NEGOTIATE_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/15332256-522e-4a53-8cd7-0bd17678a2f7)
|
78
|
+
class NegotiateContext < BinData::Record
|
79
|
+
# The NegotiateContext Data field contains a list of preauthentication integrity hash functions as well as an optional salt value, as specified in section 2.2.3.1.1.
|
80
|
+
SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001
|
81
|
+
# The NegotiateContext Data field contains a list of encryption algorithms, as specified in section 2.2.3.1.2.
|
82
|
+
SMB2_ENCRYPTION_CAPABILITIES = 0x0002
|
83
|
+
# The NegotiateContext Data field contains a list of compression algorithms, as specified in section 2.2.3.1.3.
|
84
|
+
SMB2_COMPRESSION_CAPABILITIES = 0x0003
|
85
|
+
# The NegotiateContext Data field contains the server name to which the client connects.
|
86
|
+
SMB2_NETNAME_NEGOTIATE_CONTEXT_ID = 0x0005
|
87
|
+
|
88
|
+
endian :little
|
89
|
+
|
90
|
+
string :pad, label: 'Padding', length: -> { pad_length }
|
91
|
+
uint16 :context_type, label: 'Context Type'
|
92
|
+
uint16 :data_length, label: 'Data Length', initial_value: -> { data.num_bytes }
|
93
|
+
uint32 :reserved, label: 'Reserved', initial_value: 0
|
94
|
+
choice :data, label: 'Data', selection: -> { context_type } do
|
95
|
+
preauth_integrity_capabilities SMB2_PREAUTH_INTEGRITY_CAPABILITIES, label: 'Preauthentication Integrity Capabilities'
|
96
|
+
encryption_capabilities SMB2_ENCRYPTION_CAPABILITIES, label: 'Encryption Capabilities'
|
97
|
+
compression_capabilities SMB2_COMPRESSION_CAPABILITIES, label: 'Compression Capabilities'
|
98
|
+
netname_negotiate_context_id SMB2_NETNAME_NEGOTIATE_CONTEXT_ID, label: 'Netname Negotiate Context ID'
|
99
|
+
end
|
100
|
+
|
101
|
+
def pad_length
|
102
|
+
offset = pad.abs_offset % 8
|
103
|
+
(8 - offset) % 8
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/ruby_smb/smb2/packet.rb
CHANGED
@@ -30,6 +30,8 @@ module RubySMB
|
|
30
30
|
require 'ruby_smb/smb2/packet/write_response'
|
31
31
|
require 'ruby_smb/smb2/packet/ioctl_request'
|
32
32
|
require 'ruby_smb/smb2/packet/ioctl_response'
|
33
|
+
require 'ruby_smb/smb2/packet/transform_header'
|
34
|
+
require 'ruby_smb/smb2/packet/compression_transform_header'
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module SMB2
|
3
|
+
module Packet
|
4
|
+
# An SMB2 COMPRESSION_TRANSFORM_HEADER Packet as defined in
|
5
|
+
# [2.2.42 SMB2 COMPRESSION_TRANSFORM_HEADER](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/1d435f21-9a21-4f4c-828e-624a176cf2a0)
|
6
|
+
class CompressionTransformHeader < BinData::Record
|
7
|
+
endian :little
|
8
|
+
|
9
|
+
bit32 :protocol, label: 'Protocol ID Field', initial_value: 0xFC534D42
|
10
|
+
uint32 :original_compressed_segment_size, label: 'Original Compressed Segment Size'
|
11
|
+
uint16 :compression_algorithm, label: 'Compression Algorithm'
|
12
|
+
uint16 :flags, label: 'Flags'
|
13
|
+
uint32 :offset, label: 'Offset / Length'
|
14
|
+
end
|
15
|
+
|
16
|
+
# An SMB2 SMB2_COMPRESSION_TRANSFORM_HEADER_PAYLOAD Packet as defined in
|
17
|
+
# [2.2.42.1 SMB2_COMPRESSION_TRANSFORM_HEADER_PAYLOAD](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/8898e8e7-f1b2-47f5-a525-2ce5bad6db64)
|
18
|
+
class Smb2CompressionPayloadHeader < BinData::Record
|
19
|
+
endian :little
|
20
|
+
hide :reserved
|
21
|
+
|
22
|
+
uint16 :algorithm_id, label: 'Algorithm ID'
|
23
|
+
uint16 :reserved
|
24
|
+
uint32 :payload_length, label: 'Compressed Payload Length'
|
25
|
+
end
|
26
|
+
|
27
|
+
# An SMB2 SMB2_COMPRESSION_PATTERN_PAYLOAD_V1 Packet as defined in
|
28
|
+
# [2.2.42.2 SMB2_COMPRESSION_PATTERN_PAYLOAD_V1](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/f6859837-395a-4d0a-8971-1fc3919e2d09)
|
29
|
+
class Smb2CompressionPatternPayloadV1 < BinData::Record
|
30
|
+
endian :little
|
31
|
+
hide :reserved1, :reserved2
|
32
|
+
|
33
|
+
uint8 :pattern, label: 'Pattern'
|
34
|
+
uint8 :reserved1
|
35
|
+
uint16 :reserved2
|
36
|
+
uint32 :repetitions, label: 'Repetitions'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -1,18 +1,23 @@
|
|
1
1
|
module RubySMB
|
2
2
|
module SMB2
|
3
3
|
module Packet
|
4
|
-
#
|
4
|
+
# This class represents an SMB2 Error Response Packet as defined in
|
5
|
+
# [2.2.2 SMB2 ERROR Response](https://msdn.microsoft.com/en-us/library/cc246530.aspx)
|
5
6
|
class ErrorPacket < RubySMB::GenericPacket
|
6
7
|
attr_accessor :original_command
|
7
8
|
|
8
9
|
endian :little
|
9
10
|
smb2_header :smb2_header
|
10
|
-
uint16 :structure_size,
|
11
|
-
uint8 :
|
11
|
+
uint16 :structure_size, label: 'Structure Size', initial_value: 9
|
12
|
+
uint8 :error_context_count, label: 'ErrorContextCount'
|
13
|
+
uint8 :reserved
|
14
|
+
uint32 :byte_count, label: 'Byte Count of ErrorData'
|
15
|
+
string :error_data, label: 'Error Data', read_length: -> { byte_count }
|
12
16
|
|
13
17
|
def valid?
|
14
18
|
return smb2_header.protocol == RubySMB::SMB2::SMB2_PROTOCOL_ID &&
|
15
|
-
|
19
|
+
smb2_header.command == @original_command &&
|
20
|
+
structure_size == 9
|
16
21
|
end
|
17
22
|
end
|
18
23
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ruby_smb/smb2/negotiate_context'
|
2
|
+
|
1
3
|
module RubySMB
|
2
4
|
module SMB2
|
3
5
|
module Packet
|
@@ -8,23 +10,30 @@ module RubySMB
|
|
8
10
|
|
9
11
|
endian :little
|
10
12
|
smb2_header :smb2_header
|
11
|
-
uint16 :structure_size,
|
12
|
-
uint16 :dialect_count,
|
13
|
-
smb2_security_mode :security_mode
|
14
|
-
uint16 :reserved1,
|
15
|
-
smb2_capabilities :capabilities
|
16
|
-
string :client_guid,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
uint16 :structure_size, label: 'Structure Size', initial_value: 36
|
14
|
+
uint16 :dialect_count, label: 'Dialect Count', initial_value: -> { dialects.size }
|
15
|
+
smb2_security_mode :security_mode, label: 'Security Mode'
|
16
|
+
uint16 :reserved1, label: 'Reserved', initial_value: 0
|
17
|
+
smb2_capabilities :capabilities, label: 'Capabilities'
|
18
|
+
string :client_guid, label: 'Client GUID', length: 16
|
19
|
+
struct :negotiate_context_info, label: 'Negotiate Context Info', onlyif: -> { has_negotiate_context? } do
|
20
|
+
uint32 :negotiate_context_offset, label: 'Negotiate Context Offset', initial_value: -> { negotiate_context_list.abs_offset }
|
21
|
+
uint16 :negotiate_context_count, label: 'Negotiate Context Count', initial_value: -> { negotiate_context_list.size }
|
22
|
+
uint16 :reserved2, label: 'Reserved', initial_value: 0
|
23
|
+
end
|
24
|
+
file_time :client_start_time, label: 'Client Start Time', initial_value: 0, onlyif: -> { !has_negotiate_context? }
|
25
|
+
array :dialects, label: 'Dialects', type: :uint16, initial_length: -> { dialect_count }
|
26
|
+
string :pad, label: 'Padding', length: -> { pad_length(self.dialects) }, onlyif: -> { has_negotiate_context? }
|
27
|
+
array :negotiate_context_list, label: 'Negotiate Context List', type: :negotiate_context, onlyif: -> { has_negotiate_context? }, read_until: :eof
|
28
|
+
|
29
|
+
# Adds a dialect to the Dialects array
|
21
30
|
#
|
22
31
|
# @param [Fixnum] the numeric code for the dialect you wish to add
|
23
32
|
# @return [Array<Fixnum>] the array of all currently selected dialects
|
33
|
+
# @raise [ArgumentError] if the dialect is not an Integer
|
24
34
|
def add_dialect(dialect)
|
25
|
-
|
26
|
-
self.
|
27
|
-
dialects << dialect
|
35
|
+
raise ArgumentError, 'Must be a number' unless dialect.is_a? Integer
|
36
|
+
self.dialects << dialect
|
28
37
|
end
|
29
38
|
|
30
39
|
# Takes an array of dialects and sets it on the packet. Also updates
|
@@ -35,12 +44,40 @@ module RubySMB
|
|
35
44
|
# @return [Array<Fixnum>] the current value of the dialects array
|
36
45
|
def set_dialects(add_dialects = [])
|
37
46
|
self.dialects = []
|
38
|
-
self.dialect_count = 0
|
39
47
|
add_dialects.each do |dialect|
|
40
48
|
add_dialect(dialect)
|
41
49
|
end
|
42
50
|
dialects
|
43
51
|
end
|
52
|
+
|
53
|
+
# Adds a Negotiate Context to the #negotiate_context_list
|
54
|
+
#
|
55
|
+
# @param [NegotiateContext] the Negotiate Context structure you wish to add
|
56
|
+
# @return [Array<Fixnum>] the array of all currently added Negotiate Contexts
|
57
|
+
# @raise [ArgumentError] if the dialect is not a NegotiateContext structure
|
58
|
+
def add_negotiate_context(nc)
|
59
|
+
raise ArgumentError, 'Must be a NegotiateContext' unless nc.is_a? NegotiateContext
|
60
|
+
previous_element = negotiate_context_list.last || negotiate_context_list
|
61
|
+
pad_length = pad_length(previous_element)
|
62
|
+
self.negotiate_context_list << nc
|
63
|
+
self.negotiate_context_list.last.pad = "\x00" * pad_length
|
64
|
+
self.negotiate_context_list
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Determines the correct length for the padding, so that the next
|
71
|
+
# field is 8-byte aligned.
|
72
|
+
def pad_length(prev_element)
|
73
|
+
offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 8
|
74
|
+
(8 - offset) % 8
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return true if the dialect version requires Negotiate Contexts
|
78
|
+
def has_negotiate_context?
|
79
|
+
dialects.any? { |dialect| dialect.to_i == 0x0311 }
|
80
|
+
end
|
44
81
|
end
|
45
82
|
end
|
46
83
|
end
|