ruby_smb 2.0.4 → 2.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.github/workflows/verify.yml +56 -0
  5. data/README.md +0 -1
  6. data/examples/delete_file.rb +0 -0
  7. data/examples/net_share_enum_all.rb +0 -0
  8. data/examples/pipes.rb +0 -0
  9. data/examples/rename_file.rb +0 -0
  10. data/lib/ruby_smb.rb +3 -2
  11. data/lib/ruby_smb/client.rb +25 -21
  12. data/lib/ruby_smb/client/negotiation.rb +10 -12
  13. data/lib/ruby_smb/compression.rb +7 -0
  14. data/lib/ruby_smb/compression/lznt1.rb +164 -0
  15. data/lib/ruby_smb/dcerpc.rb +3 -1
  16. data/lib/ruby_smb/dcerpc/ndr.rb +97 -0
  17. data/lib/ruby_smb/dcerpc/netlogon.rb +101 -0
  18. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request.rb +37 -0
  19. data/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response.rb +26 -0
  20. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request.rb +37 -0
  21. data/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response.rb +23 -0
  22. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request.rb +32 -0
  23. data/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response.rb +24 -0
  24. data/lib/ruby_smb/dcerpc/request.rb +6 -0
  25. data/lib/ruby_smb/dispatcher/socket.rb +1 -1
  26. data/lib/ruby_smb/smb1/file.rb +1 -1
  27. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +0 -1
  28. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +0 -1
  29. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +1 -2
  30. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +1 -13
  31. data/lib/ruby_smb/smb1/pipe.rb +4 -2
  32. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +4 -0
  33. data/lib/ruby_smb/smb2/pipe.rb +6 -4
  34. data/lib/ruby_smb/version.rb +1 -1
  35. data/spec/lib/ruby_smb/client_spec.rb +17 -7
  36. data/spec/lib/ruby_smb/compression/lznt1_spec.rb +32 -0
  37. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request_spec.rb +69 -0
  38. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response_spec.rb +53 -0
  39. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_request_spec.rb +69 -0
  40. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_password_set2_response_spec.rb +37 -0
  41. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request_spec.rb +45 -0
  42. data/spec/lib/ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response_spec.rb +37 -0
  43. data/spec/lib/ruby_smb/smb1/file_spec.rb +1 -1
  44. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +0 -1
  45. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +0 -1
  46. data/spec/lib/ruby_smb/smb1/packet/trans2/open2_response_spec.rb +0 -5
  47. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +0 -6
  48. data/spec/spec_helper.rb +1 -1
  49. metadata +42 -19
  50. metadata.gz.sig +0 -0
  51. data/.travis.yml +0 -6
@@ -7,6 +7,9 @@ module RubySMB
7
7
  VER_MAJOR = 2
8
8
  VER_MINOR = 0
9
9
 
10
+ # An NDR Enum type as defined in
11
+ # [Transfer Syntax NDR - Enumerated Types](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_02_05_01)
12
+ class NdrEnum < BinData::Int16le; end
10
13
 
11
14
  # An NDR Conformant and Varying String representation as defined in
12
15
  # [Transfer Syntax NDR - Conformant and Varying Strings](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02)
@@ -92,6 +95,100 @@ module RubySMB
92
95
  end
93
96
  end
94
97
 
98
+ # An NDR Uni-dimensional Fixed Array of bytes representation as defined in:
99
+ # [Transfer Syntax NDR - NDR Constructed Types](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_01)
100
+ class NdrFixedByteArray < BinData::BasePrimitive
101
+ optional_parameters :read_length, :length, :pad_byte, :pad_front
102
+ default_parameters pad_byte: 0
103
+ mutually_exclusive_parameters :length, :value
104
+
105
+ def initialize_shared_instance
106
+ if (has_parameter?(:value) || has_parameter?(:asserted_value)) && !has_parameter?(:read_length)
107
+ extend WarnNoReadLengthPlugin
108
+ end
109
+ super
110
+ end
111
+
112
+ def assign(val)
113
+ super(fixed_byte_array(val))
114
+ end
115
+
116
+ def snapshot
117
+ clamp_to_length(super)
118
+ end
119
+
120
+ class << self
121
+ def arg_processor
122
+ NdrFixedByteArrayArgProcessor.new
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def clamp_to_length(val)
129
+ val = fixed_byte_array(val)
130
+ len = eval_parameter(:length) || val.length
131
+ if val.length > len
132
+ val = val.first(len)
133
+ elsif val.length < len
134
+ pad = eval_parameter(:pad_byte)
135
+ if get_parameter(:pad_front)
136
+ val = val.insert(0, *Array.new(len - val.length, pad))
137
+ else
138
+ val = val.fill(pad, val.length...len)
139
+ end
140
+ end
141
+
142
+ val
143
+ end
144
+
145
+ def fixed_byte_array(val)
146
+ val = val.bytes if val.is_a? String
147
+ val.to_ary
148
+ end
149
+
150
+ def read_and_return_value(io)
151
+ len = eval_parameter(:read_length) || eval_parameter(:length) || 0
152
+ io.readbytes(len)
153
+ end
154
+
155
+ def sensible_default
156
+ [ ]
157
+ end
158
+
159
+ def value_to_binary_string(val)
160
+ clamp_to_length(val).pack('C*')
161
+ end
162
+
163
+ class NdrFixedByteArrayArgProcessor < BinData::BaseArgProcessor
164
+ def sanitize_parameters!(obj_class, obj_params)
165
+ obj_params.must_be_integer(:length, :pad_byte)
166
+ obj_params.sanitize(:pad_byte) { |byte| sanitized_pad_byte(byte) }
167
+ end
168
+
169
+ private
170
+
171
+ def sanitized_pad_byte(byte)
172
+ if byte.is_a?(String)
173
+ raise ArgumentError, ':pad_byte must not contain more than 1 byte' if byte.bytesize > 1
174
+
175
+ byte = byte.ord
176
+ end
177
+ raise ArgumentError, ':pad_byte must be within the range of 0 - 255' unless ((byte >= 0) && (byte <= 255))
178
+
179
+ byte
180
+ end
181
+ end
182
+
183
+ # Warns when reading if :value && no :read_length
184
+ module WarnNoReadLengthPlugin
185
+ def read_and_return_value(io)
186
+ warn "#{debug_name} does not have a :read_length parameter - returning empty array"
187
+ ""
188
+ end
189
+ end
190
+ end
191
+
95
192
  # An NDR Context Handle representation as defined in
96
193
  # [IDL Data Type Declarations - Basic Type Declarations](http://pubs.opengroup.org/onlinepubs/9629399/apdxn.htm#tagcjh_34_01)
97
194
  class NdrContextHandle < BinData::Primitive
@@ -0,0 +1,101 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Netlogon
4
+
5
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/592edbc8-f6f1-40c0-9ab3-fe6725ac6d7e
6
+ UUID = '12345678-1234-abcd-ef00-01234567cffb'
7
+ VER_MAJOR = 1
8
+ VER_MINOR = 0
9
+
10
+ # Operation numbers
11
+ NETR_SERVER_REQ_CHALLENGE = 4
12
+ NETR_SERVER_AUTHENTICATE3 = 26
13
+ NETR_SERVER_PASSWORD_SET2 = 30
14
+
15
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/3b224201-b531-43e2-8c79-b61f6dea8640
16
+ class LogonsrvHandle < Ndr::NdrLpStr; end
17
+
18
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/d55e2632-7163-4f6c-b662-4b870e8cc1cd
19
+ class NetlogonCredential < Ndr::NdrFixedByteArray
20
+ default_parameters length: 8
21
+ end
22
+
23
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/76c93227-942a-4687-ab9d-9d972ffabdab
24
+ class NetlogonAuthenticator < BinData::Record
25
+ endian :little
26
+
27
+ netlogon_credential :credential
28
+ uint32 :timestamp
29
+ end
30
+
31
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/4d1235e3-2c96-4e9f-a147-3cb338a0d09f
32
+ class NetlogonSecureChannelType < Ndr::NdrEnum
33
+ # enum example from dmendel/bindata#38 https://github.com/dmendel/bindata/issues/38#issuecomment-46397163
34
+ ALL = {
35
+ 0 => :NullSecureChannel,
36
+ 1 => :MsvApSecureChannel,
37
+ 2 => :WorkstationSecureChannel,
38
+ 3 => :TrustedDnsDomainSecureChannel,
39
+ 4 => :TrustedDomainSecureChannel,
40
+ 5 => :UasServerSecureChannel,
41
+ 6 => :ServerSecureChannel,
42
+ 7 => :CdcServerSecureChannel
43
+ }
44
+ ALL.each_pair { |val,sym| const_set(sym.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').upcase, val) }
45
+ default_parameter assert: -> { ALL.keys.include? value }
46
+
47
+ def as_enum
48
+ ALL[value]
49
+ end
50
+
51
+ def assign(val)
52
+ if val.is_a? Symbol
53
+ val = ALL.key(val)
54
+ raise ArgumentError, 'invalid value name' if val.nil?
55
+ end
56
+
57
+ super
58
+ end
59
+ end
60
+
61
+ require 'ruby_smb/dcerpc/netlogon/netr_server_authenticate3_request'
62
+ require 'ruby_smb/dcerpc/netlogon/netr_server_authenticate3_response'
63
+ require 'ruby_smb/dcerpc/netlogon/netr_server_password_set2_request'
64
+ require 'ruby_smb/dcerpc/netlogon/netr_server_password_set2_response'
65
+ require 'ruby_smb/dcerpc/netlogon/netr_server_req_challenge_request'
66
+ require 'ruby_smb/dcerpc/netlogon/netr_server_req_challenge_response'
67
+
68
+ # Calculate the netlogon session key from the provided shared secret and
69
+ # challenges. The shared secret is an NTLM hash.
70
+ #
71
+ # @param shared_secret [String] the share secret between the client and the server
72
+ # @param client_challenge [String] the client challenge portion of the negotiation
73
+ # @param server_challenge [String] the server challenge portion of the negotiation
74
+ # @return [String] the session key for encryption
75
+ def self.calculate_session_key(shared_secret, client_challenge, server_challenge)
76
+ client_challenge = client_challenge.to_binary_s if client_challenge.is_a? NetlogonCredential
77
+ server_challenge = server_challenge.to_binary_s if server_challenge.is_a? NetlogonCredential
78
+
79
+ hmac = OpenSSL::HMAC.new(shared_secret, OpenSSL::Digest::SHA256.new)
80
+ hmac << client_challenge
81
+ hmac << server_challenge
82
+ hmac.digest.first(16)
83
+ end
84
+
85
+ # Encrypt the input data using the specified session key. This is used for
86
+ # certain Netlogon service operations including the authentication
87
+ # process. Per the specification, this uses AES-128-CFB8 with an all zero
88
+ # initialization vector.
89
+ #
90
+ # @param session_key [String] the session key to use for encryption (must be 16 bytes long)
91
+ # @param input_data [String] the data to encrypt
92
+ # @return [String] the encrypted data
93
+ def self.encrypt_credential(session_key, input_data)
94
+ cipher = OpenSSL::Cipher.new('AES-128-CFB8').encrypt
95
+ cipher.iv = "\x00" * 16
96
+ cipher.key = session_key
97
+ cipher.update(input_data) + cipher.final
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,37 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.2 NetrServerAuthenticate3 (Opnum 26)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/3a9ed16f-8014-45ae-80af-c0ecb06e2db9)
8
+ class NetrServerAuthenticate3Request < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ logonsrv_handle :primary_name
14
+ string :pad1, length: -> { pad_length(self.primary_name) }
15
+ ndr_string :account_name
16
+ netlogon_secure_channel_type :secure_channel_type
17
+ string :pad2, length: -> { pad_length(self.secure_channel_type) }
18
+ ndr_string :computer_name
19
+ netlogon_credential :client_credential
20
+ string :pad3, length: -> { pad_length(self.client_credential) }
21
+ uint32 :flags
22
+
23
+ def initialize_instance
24
+ super
25
+ @opnum = NETR_SERVER_AUTHENTICATE3
26
+ end
27
+
28
+ # Determines the correct length for the padding, so that the next
29
+ # field is 4-byte aligned.
30
+ def pad_length(prev_element)
31
+ offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
32
+ (4 - offset) % 4
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.2 NetrServerAuthenticate3 (Opnum 26)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/3a9ed16f-8014-45ae-80af-c0ecb06e2db9)
8
+ class NetrServerAuthenticate3Response < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ netlogon_credential :server_credential
14
+ uint32 :negotiate_flags
15
+ uint32 :account_rid
16
+ uint32 :error_status
17
+
18
+ def initialize_instance
19
+ super
20
+ @opnum = NETR_SERVER_AUTHENTICATE3
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,37 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.5 NetrServerPasswordSet2 (Opnum 30)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/14b020a8-0bcf-4af5-ab72-cc92bc6b1d81)
8
+ class NetrServerPasswordSet2Request < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ logonsrv_handle :primary_name
14
+ string :pad1, length: -> { pad_length(self.primary_name) }
15
+ ndr_string :account_name
16
+ netlogon_secure_channel_type :secure_channel_type
17
+ string :pad2, length: -> { pad_length(self.secure_channel_type) }
18
+ ndr_string :computer_name
19
+ string :pad3, length: -> { pad_length(self.computer_name) }
20
+ netlogon_authenticator :authenticator
21
+ ndr_fixed_byte_array :clear_new_password, length: 516 # this is an encrypted NL_TRUST_PASSWORD
22
+
23
+ def initialize_instance
24
+ super
25
+ @opnum = Netlogon::NETR_SERVER_PASSWORD_SET2
26
+ end
27
+
28
+ # Determines the correct length for the padding, so that the next
29
+ # field is 4-byte aligned.
30
+ def pad_length(prev_element)
31
+ offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
32
+ (4 - offset) % 4
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.5 NetrServerPasswordSet2 (Opnum 30)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/14b020a8-0bcf-4af5-ab72-cc92bc6b1d81)
8
+ class NetrServerPasswordSet2Response < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ netlogon_authenticator :return_authenticator
14
+ uint32 :error_status
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = Netlogon::NETR_SERVER_PASSWORD_SET2
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.1 NetrServerReqChallenge (Opnum 4)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/5ad9db9f-7441-4ce5-8c7b-7b771e243d32)
8
+ class NetrServerReqChallengeRequest < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ logonsrv_handle :primary_name
14
+ string :pad1, length: -> { pad_length(self.primary_name) }
15
+ ndr_string :computer_name
16
+ netlogon_credential :client_challenge
17
+
18
+ def initialize_instance
19
+ super
20
+ @opnum = NETR_SERVER_REQ_CHALLENGE
21
+ end
22
+
23
+ # Determines the correct length for the padding, so that the next
24
+ # field is 4-byte aligned.
25
+ def pad_length(prev_element)
26
+ offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 4
27
+ (4 - offset) % 4
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ module RubySMB
4
+ module Dcerpc
5
+ module Netlogon
6
+
7
+ # [3.5.4.4.1 NetrServerReqChallenge (Opnum 4)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/5ad9db9f-7441-4ce5-8c7b-7b771e243d32)
8
+ class NetrServerReqChallengeResponse < BinData::Record
9
+ attr_reader :opnum
10
+
11
+ endian :little
12
+
13
+ netlogon_credential :server_challenge
14
+ uint32 :error_status
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = NETR_SERVER_REQ_CHALLENGE
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -31,6 +31,12 @@ module RubySMB
31
31
  save_key_request RubySMB::Dcerpc::Winreg::REG_SAVE_KEY
32
32
  string :default
33
33
  end
34
+ choice 'Netlogon', selection: -> { opnum } do
35
+ netr_server_authenticate3_request RubySMB::Dcerpc::Netlogon::NETR_SERVER_AUTHENTICATE3
36
+ netr_server_password_set2_request RubySMB::Dcerpc::Netlogon::NETR_SERVER_PASSWORD_SET2
37
+ netr_server_req_challenge_request RubySMB::Dcerpc::Netlogon::NETR_SERVER_REQ_CHALLENGE
38
+ string :default
39
+ end
34
40
  choice 'Srvsvc', selection: -> { opnum } do
35
41
  net_share_enum_all RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL, host: -> { host rescue '' }
36
42
  string :default
@@ -55,7 +55,7 @@ module RubySMB
55
55
 
56
56
  # Read a packet off the wire and parse it into a string
57
57
  #
58
- # @param full_response [Boolean] whether to include the NetBios Session Service header in the repsonse
58
+ # @param full_response [Boolean] whether to include the NetBios Session Service header in the response
59
59
  # @return [String] the raw response (including the NetBios Session Service header if full_response is true)
60
60
  # @raise [RubySMB::Error::NetBiosSessionService] if there's an error reading the first 4 bytes,
61
61
  # which are assumed to be the NetBiosSessionService header.
@@ -301,7 +301,7 @@ module RubySMB
301
301
  passthrough_info_level = RubySMB::Fscc::FileInformation::FILE_RENAME_INFORMATION +
302
302
  RubySMB::Fscc::FileInformation::SMB_INFO_PASSTHROUGH
303
303
  rename_request.data_block.trans2_parameters.information_level = passthrough_info_level
304
- rename_request.data_block.trans2_data.info_level_struct.file_name = new_file_name.encode('utf-16le')
304
+ rename_request.data_block.trans2_data.info_level_struct.file_name = new_file_name
305
305
  set_trans2_params(rename_request)
306
306
  end
307
307
 
@@ -40,7 +40,6 @@ module RubySMB
40
40
 
41
41
  # The {RubySMB::SMB1::DataBlock} specific to this packet type.
42
42
  class DataBlock < RubySMB::SMB1::Packet::Trans2::DataBlock
43
- uint8 :name, label: 'Name', initial_value: 0x00
44
43
  string :pad1, length: -> { pad1_length }
45
44
  trans2_parameters :trans2_parameters, label: 'Trans2 Parameters'
46
45
  string :pad2, length: -> { pad2_length }
@@ -39,7 +39,6 @@ module RubySMB
39
39
 
40
40
  # The {RubySMB::SMB1::DataBlock} specific to this packet type.
41
41
  class DataBlock < RubySMB::SMB1::Packet::Trans2::DataBlock
42
- uint8 :name, label: 'Name', initial_value: 0x00
43
42
  string :pad1, length: -> { pad1_length }
44
43
  trans2_parameters :trans2_parameters, label: 'Trans2 Parameters'
45
44
  string :pad2, length: -> { pad2_length }