ruby_smb 3.1.2 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/examples/file_server.rb +6 -68
  4. data/examples/virtual_file_server.rb +10 -62
  5. data/lib/ruby_smb/client/authentication.rb +29 -4
  6. data/lib/ruby_smb/client/negotiation.rb +2 -0
  7. data/lib/ruby_smb/client.rb +18 -3
  8. data/lib/ruby_smb/dcerpc/error.rb +13 -0
  9. data/lib/ruby_smb/dcerpc/fault.rb +83 -0
  10. data/lib/ruby_smb/dcerpc/ndr.rb +19 -8
  11. data/lib/ruby_smb/dcerpc/request.rb +15 -10
  12. data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +5 -0
  13. data/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_request.rb +24 -0
  14. data/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_response.rb +24 -0
  15. data/lib/ruby_smb/dcerpc/samr/samr_delete_user_request.rb +21 -0
  16. data/lib/ruby_smb/dcerpc/samr/samr_delete_user_response.rb +22 -0
  17. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_request.rb +25 -0
  18. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_response.rb +25 -0
  19. data/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response.rb +0 -31
  20. data/lib/ruby_smb/dcerpc/samr/samr_get_alias_membership_response.rb +1 -14
  21. data/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_request.rb +23 -0
  22. data/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_response.rb +23 -0
  23. data/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_request.rb +23 -0
  24. data/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_response.rb +21 -0
  25. data/lib/ruby_smb/dcerpc/samr.rb +453 -83
  26. data/lib/ruby_smb/dcerpc.rb +1 -0
  27. data/lib/ruby_smb/error.rb +4 -0
  28. data/lib/ruby_smb/gss.rb +1 -0
  29. data/lib/ruby_smb/ntlm/client.rb +74 -0
  30. data/lib/ruby_smb/ntlm.rb +1 -0
  31. data/lib/ruby_smb/server/cli.rb +121 -0
  32. data/lib/ruby_smb/server.rb +1 -0
  33. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +11 -0
  34. data/lib/ruby_smb/smb1/pipe.rb +4 -1
  35. data/lib/ruby_smb/smb2/pipe.rb +4 -2
  36. data/lib/ruby_smb/version.rb +1 -1
  37. data/spec/lib/ruby_smb/client_spec.rb +1 -0
  38. data/spec/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_request_spec.rb +69 -0
  39. data/spec/lib/ruby_smb/dcerpc/samr/samr_create_user2_in_domain_response_spec.rb +69 -0
  40. data/spec/lib/ruby_smb/dcerpc/samr/samr_delete_user_request_spec.rb +42 -0
  41. data/spec/lib/ruby_smb/dcerpc/samr/samr_delete_user_response_spec.rb +51 -0
  42. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_request_spec.rb +60 -0
  43. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_domains_in_sam_server_response_spec.rb +75 -0
  44. data/spec/lib/ruby_smb/dcerpc/samr/samr_enumerate_users_in_domain_response_spec.rb +0 -195
  45. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_request_spec.rb +62 -0
  46. data/spec/lib/ruby_smb/dcerpc/samr/samr_lookup_names_in_domain_response_spec.rb +54 -0
  47. data/spec/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_request_spec.rb +67 -0
  48. data/spec/lib/ruby_smb/dcerpc/samr/samr_set_information_user2_response_spec.rb +35 -0
  49. data/spec/lib/ruby_smb/dcerpc/samr_spec.rb +194 -0
  50. data/spec/lib/ruby_smb/ntlm/client/session_spec.rb +114 -0
  51. data/spec/lib/ruby_smb/ntlm/client_spec.rb +36 -0
  52. data.tar.gz.sig +0 -0
  53. metadata +39 -2
  54. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 498d32e1eeed4cb410f03dfdc5ea1b23dd5506d89e387a3533494b9f73294ffa
4
- data.tar.gz: 838a864709be049725a3978de10d71a0cf9eb75e91d00468cdee07fc5e614250
3
+ metadata.gz: 4749ae5185070d44e447593cd863ee5ac17a87468b16b14a251f68e0166b7663
4
+ data.tar.gz: b63378e4367136e0c6dffc35aa02b5c5ce1df450d5b64300eaee060e7938e9ec
5
5
  SHA512:
6
- metadata.gz: e60651395300c2f7bc88049e7572a2b5376646615da08a1978d8e71afe694ca2f32396e124cbda71cc203811ff8df0241aa072f58aeffa8265498789cab7696f
7
- data.tar.gz: e9d3bd5ffb781a01e4382d8f0f2327cd384d4be520a394fbf00336fbc23b9913fae58b380c05c1782c320364edd8ac5e729639091c0f54b32208469972810705
6
+ metadata.gz: f09557d4ba6148796bb6adf2c1169ebad237f021527975fabb179e1eb2099cfb2c17e45d3b57629c7fc61106092317076e41c77d39f8cb3d4335d280a75e144c
7
+ data.tar.gz: e969cc9f474e64e4cf7ba9bcf39e1dfc30b9e365bf6c758abd1e7ff905a6e9a3a8f3c3b33fc88f3104b7998201406d8b78bca34e746c58cfa4c03353bd94f789
checksums.yaml.gz.sig CHANGED
Binary file
@@ -3,81 +3,19 @@
3
3
  require 'bundler/setup'
4
4
  require 'optparse'
5
5
  require 'ruby_smb'
6
- require 'ruby_smb/gss/provider/ntlm'
7
6
 
8
7
  # we just need *a* default encoding to handle the strings from the NTLM messages
9
8
  Encoding.default_internal = 'UTF-8' if Encoding.default_internal.nil?
10
9
 
11
- options = {
12
- allow_anonymous: true,
13
- allow_guests: false,
14
- domain: nil,
15
- username: 'RubySMB',
16
- password: 'password',
17
- share_name: 'home',
18
- share_path: '.',
19
- smbv1: true,
20
- smbv2: true,
21
- smbv3: true
22
- }
23
- OptionParser.new do |opts|
24
- opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
25
- opts.on("--path PATH", "The path to share (default: #{options[:share_path]})") do |path|
10
+ options = RubySMB::Server::Cli.parse(defaults: { share_path: '.' }) do |options, parser|
11
+ parser.banner = "Usage: #{File.basename(__FILE__)} [options]"
12
+
13
+ parser.on("--share-path SHARE_PATH", "The path to share (default: #{options[:share_path]})") do |path|
26
14
  options[:share_path] = path
27
15
  end
28
- opts.on("--share SHARE", "The share name (default: #{options[:share_name]})") do |share|
29
- options[:share_name] = share
30
- end
31
- opts.on("--[no-]anonymous", "Allow anonymous access (default: #{options[:allow_anonymous]})") do |allow_anonymous|
32
- options[:allow_anonymous] = allow_anonymous
33
- end
34
- opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
35
- options[:smbv1] = smbv1
36
- end
37
- opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
38
- options[:smbv2] = smbv2
39
- end
40
- opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
41
- options[:smbv3] = smbv3
42
- end
43
- opts.on("--[no-]guests", "Allow guest accounts (default: #{options[:allow_guests]})") do |allow_guests|
44
- options[:allow_guests] = allow_guests
45
- end
46
- opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
47
- if username.include?('\\')
48
- options[:domain], options[:username] = username.split('\\', 2)
49
- else
50
- options[:username] = username
51
- end
52
- end
53
- opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
54
- options[:password] = password
55
- end
56
- end.parse!
57
-
58
- ntlm_provider = RubySMB::Gss::Provider::NTLM.new(
59
- allow_anonymous: options[:allow_anonymous],
60
- allow_guests: options[:allow_guests]
61
- )
62
- ntlm_provider.put_account(options[:username], options[:password], domain: options[:domain]) # password can also be an NTLM hash
63
-
64
- server = RubySMB::Server.new(
65
- gss_provider: ntlm_provider,
66
- logger: :stdout
67
- )
68
- server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB1 } unless options[:smbv1]
69
- server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB2 } unless options[:smbv2]
70
- server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB3 } unless options[:smbv3]
71
-
72
- if server.dialects.empty?
73
- puts "at least one version must be enabled"
74
- exit false
75
16
  end
76
17
 
18
+ server = RubySMB::Server::Cli.build(options)
77
19
  server.add_share(RubySMB::Server::Share::Provider::Disk.new(options[:share_name], options[:share_path]))
78
- puts "server is running"
79
- server.run do
80
- puts "received connection"
81
- true
82
- end
83
20
 
21
+ RubySMB::Server::Cli.run(server)
@@ -3,7 +3,6 @@
3
3
  require 'bundler/setup'
4
4
  require 'optparse'
5
5
  require 'ruby_smb'
6
- require 'ruby_smb/gss/provider/ntlm'
7
6
 
8
7
  # we just need *a* default encoding to handle the strings from the NTLM messages
9
8
  Encoding.default_internal = 'UTF-8' if Encoding.default_internal.nil?
@@ -32,70 +31,23 @@ MAGIC_8_BALL_ANSWERS = [
32
31
  'Very doubtful'
33
32
  ]
34
33
 
35
- options = {
36
- allow_anonymous: true,
37
- domain: nil,
38
- username: 'RubySMB',
39
- password: 'password',
40
- share_name: 'home',
41
- smbv1: true,
42
- smbv2: true,
43
- smbv3: true
44
- }
45
- OptionParser.new do |opts|
46
- opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
47
- opts.on("--share SHARE", "The share name (default: #{options[:share_name]})") do |share|
48
- options[:share_name] = share
49
- end
50
- opts.on("--[no-]anonymous", "Allow anonymous access (default: #{options[:allow_anonymous]})") do |allow_anonymous|
51
- options[:allow_anonymous] = allow_anonymous
52
- end
53
- opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
54
- options[:smbv1] = smbv1
55
- end
56
- opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
57
- options[:smbv2] = smbv2
58
- end
59
- opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
60
- options[:smbv3] = smbv3
61
- end
62
- opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
63
- if username.include?('\\')
64
- options[:domain], options[:username] = username.split('\\', 2)
65
- else
66
- options[:username] = username
67
- end
68
- end
69
- opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
70
- options[:password] = password
71
- end
72
- opts.on("--virtual-content CONTENT", "The virtual share contents") do |virtual_content|
34
+ options = RubySMB::Server::Cli.parse do |options, parser|
35
+ parser.banner = "Usage: #{File.basename(__FILE__)} [options]"
36
+
37
+ parser.on("--virtual-content CONTENT", "The virtual share contents") do |virtual_content|
73
38
  options[:virtual_content] = virtual_content
74
39
  end
75
- opts.on("--virtual-name NAME", "The virtual share file name") do |virtual_name|
40
+
41
+ parser.on("--virtual-name NAME", "The virtual share file name") do |virtual_name|
76
42
  options[:virtual_name] = virtual_name
77
43
  end
78
- opts.on("--virtual-type TYPE", "The virtual share type") do |virtual_type|
44
+
45
+ parser.on("--virtual-type TYPE", "The virtual share type") do |virtual_type|
79
46
  options[:virtual_type] = virtual_type
80
47
  end
81
- end.parse!
82
-
83
- ntlm_provider = RubySMB::Gss::Provider::NTLM.new(allow_anonymous: options[:allow_anonymous])
84
- ntlm_provider.put_account(options[:username], options[:password], domain: options[:domain]) # password can also be an NTLM hash
85
-
86
- server = RubySMB::Server.new(
87
- gss_provider: ntlm_provider,
88
- logger: :stdout
89
- )
90
- server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB1 } unless options[:smbv1]
91
- server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB2 } unless options[:smbv2]
92
- server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB3 } unless options[:smbv3]
93
-
94
- if server.dialects.empty?
95
- puts "at least one version must be enabled"
96
- exit false
97
48
  end
98
49
 
50
+ server = RubySMB::Server::Cli.build(options)
99
51
  virtual_disk = RubySMB::Server::Share::Provider::VirtualDisk.new(options[:share_name])
100
52
 
101
53
  # greeting is a static text file
@@ -135,9 +87,5 @@ elsif options[:virtual_content] || options[:virtual_name] || options[:virtual_ty
135
87
  end
136
88
 
137
89
  server.add_share(virtual_disk)
138
- puts "server is running"
139
- server.run do |server_client|
140
- puts "received connection"
141
- true
142
- end
143
90
 
91
+ RubySMB::Server::Cli.run(server)
@@ -80,7 +80,7 @@ module RubySMB
80
80
  type2_b64_message = smb1_type2_message(challenge_packet)
81
81
  type3_message = @ntlm_client.init_context(type2_b64_message)
82
82
 
83
- @session_key = @ntlm_client.session_key
83
+ @application_key = @session_key = @ntlm_client.session_key
84
84
  challenge_message = @ntlm_client.session.challenge_message
85
85
  store_target_info(challenge_message.target_info) if challenge_message.has_flag?(:TARGET_INFO)
86
86
  @os_version = extract_os_version(challenge_message.os_version.to_s) unless challenge_message.os_version.empty?
@@ -146,7 +146,7 @@ module RubySMB
146
146
  end
147
147
 
148
148
  # Takes the raw binary string and returns a {RubySMB::SMB1::Packet::SessionSetupResponse}
149
- def smb1_ntlmssp_final_packet(raw_response)
149
+ def smb1_session_setup_response(raw_response)
150
150
  packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
151
151
 
152
152
  unless packet.valid?
@@ -159,6 +159,11 @@ module RubySMB
159
159
  packet
160
160
  end
161
161
 
162
+ # Takes the raw binary string and returns a {RubySMB::SMB1::Packet::SessionSetupResponse}
163
+ def smb1_ntlmssp_final_packet(raw_response)
164
+ smb1_session_setup_response(raw_response)
165
+ end
166
+
162
167
  # Takes the raw binary string and returns a {RubySMB::SMB1::Packet::SessionSetupResponse}
163
168
  def smb1_ntlmssp_challenge_packet(raw_response)
164
169
  packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
@@ -205,7 +210,7 @@ module RubySMB
205
210
  type2_b64_message = smb2_type2_message(challenge_packet)
206
211
  type3_message = @ntlm_client.init_context(type2_b64_message)
207
212
 
208
- @session_key = @ntlm_client.session_key
213
+ @application_key = @session_key = @ntlm_client.session_key
209
214
  challenge_message = ntlm_client.session.challenge_message
210
215
  store_target_info(challenge_message.target_info) if challenge_message.has_flag?(:TARGET_INFO)
211
216
  @os_version = extract_os_version(challenge_message.os_version.to_s) unless challenge_message.os_version.empty?
@@ -222,6 +227,21 @@ module RubySMB
222
227
  # disable encryption when necessary
223
228
  @session_encrypt_data = false
224
229
  end
230
+
231
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/7fd079ca-17e6-4f02-8449-46b606ea289c
232
+ if @dialect == '0x0300' || @dialect == '0x0302'
233
+ @application_key = RubySMB::Crypto::KDF.counter_mode(
234
+ @session_key,
235
+ "SMB2APP\x00",
236
+ "SmbRpc\x00"
237
+ )
238
+ else
239
+ @application_key = RubySMB::Crypto::KDF.counter_mode(
240
+ @session_key,
241
+ "SMBAppKey\x00",
242
+ @preauth_integrity_hash_value
243
+ )
244
+ end
225
245
  # otherwise, leave encryption to the default value that it was initialized to
226
246
  end
227
247
  ######
@@ -235,7 +255,7 @@ module RubySMB
235
255
  end
236
256
 
237
257
  # Takes the raw binary string and returns a {RubySMB::SMB2::Packet::SessionSetupResponse}
238
- def smb2_ntlmssp_final_packet(raw_response)
258
+ def smb2_session_setup_response(raw_response)
239
259
  packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
240
260
  unless packet.valid?
241
261
  raise RubySMB::Error::InvalidPacket.new(
@@ -248,6 +268,11 @@ module RubySMB
248
268
  packet
249
269
  end
250
270
 
271
+ # Takes the raw binary string and returns a {RubySMB::SMB2::Packet::SessionSetupResponse}
272
+ def smb2_ntlmssp_final_packet(raw_response)
273
+ smb2_session_setup_response(raw_response)
274
+ end
275
+
251
276
  # Takes the raw binary string and returns a {RubySMB::SMB2::Packet::SessionSetupResponse}
252
277
  def smb2_ntlmssp_challenge_packet(raw_response)
253
278
  packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
@@ -118,6 +118,7 @@ module RubySMB
118
118
  self.server_max_buffer_size = packet.parameter_block.max_buffer_size - 260
119
119
  self.negotiated_smb_version = 1
120
120
  self.session_encrypt_data = false
121
+ self.negotiation_security_buffer = packet.data_block.security_blob
121
122
  'SMB1'
122
123
  when RubySMB::SMB2::Packet::NegotiateResponse
123
124
  self.smb1 = false
@@ -137,6 +138,7 @@ module RubySMB
137
138
  self.server_start_time = packet.server_start_time.to_time if packet.server_start_time != 0
138
139
  self.server_system_time = packet.system_time.to_time if packet.system_time != 0
139
140
  self.server_supports_multi_credit = self.dialect != '0x0202' && packet&.capabilities&.large_mtu == 1
141
+ self.negotiation_security_buffer = packet.security_buffer
140
142
  case self.dialect
141
143
  when '0x02ff'
142
144
  when '0x0300', '0x0302'
@@ -54,6 +54,13 @@ module RubySMB
54
54
  # The default maximum size of a SMB message that the Server accepts (in bytes)
55
55
  SERVER_MAX_BUFFER_SIZE = 4356
56
56
 
57
+ # The application key. After authenticating to the remote server, this value is the session key for dialects less
58
+ # than version 3 and a unique value for v3 dialects. See:
59
+ # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/901ae284-31d3-4ea1-ae8a-766fc8bfe00e
60
+ # @!attribute [rw] application_key
61
+ # @return [String]
62
+ attr_accessor :application_key
63
+
57
64
  # The dispatcher responsible for sending packets
58
65
  # @!attribute [rw] dispatcher
59
66
  # @return [RubySMB::Dispatcher::Socket]
@@ -288,13 +295,19 @@ module RubySMB
288
295
  attr_accessor :negotiated_smb_version
289
296
 
290
297
  # Whether or not the server supports multi-credit operations. It is
291
- # reported by the LARGE_MTU capabiliy as part of the negotiation process
298
+ # reported by the LARGE_MTU capability as part of the negotiation process
292
299
  # (SMB 2.x and 3.x).
293
300
  # @!attribute [rw] server_supports_multi_credit
294
301
  # @return [Boolean] true if the server supports multi-credit operations,
295
302
  # false otherwise
296
303
  attr_accessor :server_supports_multi_credit
297
304
 
305
+ # The negotiated security buffer. This is nil until the negotiation process
306
+ # has finished.
307
+ # @!attribute [rw] negotiation_security_buffer
308
+ # @return [String] The raw security buffer bytes
309
+ attr_accessor :negotiation_security_buffer
310
+
298
311
  # @param dispatcher [RubySMB::Dispatcher::Socket] the packet dispatcher to use
299
312
  # @param smb1 [Boolean] whether or not to enable SMB1 support
300
313
  # @param smb2 [Boolean] whether or not to enable SMB2 support
@@ -313,6 +326,7 @@ module RubySMB
313
326
  @sequence_counter = 0
314
327
  @session_id = 0x00
315
328
  @session_key = ''
329
+ @application_key = ''
316
330
  @session_is_guest = false
317
331
  @signing_required = false
318
332
  @smb1 = smb1
@@ -332,7 +346,7 @@ module RubySMB
332
346
  # session setup response is received
333
347
  @session_encrypt_data = always_encrypt
334
348
 
335
- @ntlm_client = Net::NTLM::Client.new(
349
+ @ntlm_client = RubySMB::NTLM::Client.new(
336
350
  @username,
337
351
  @password,
338
352
  workstation: @local_workstation,
@@ -404,7 +418,7 @@ module RubySMB
404
418
  @password = pass.encode('utf-8') || ''.encode('utf-8')
405
419
  @username = user.encode('utf-8') || ''.encode('utf-8')
406
420
 
407
- @ntlm_client = Net::NTLM::Client.new(
421
+ @ntlm_client = RubySMB::NTLM::Client.new(
408
422
  @username,
409
423
  @password,
410
424
  workstation: @local_workstation,
@@ -617,6 +631,7 @@ module RubySMB
617
631
  def wipe_state!
618
632
  self.session_id = 0x00
619
633
  self.user_id = 0x00
634
+ self.application_key = ''
620
635
  self.session_key = ''
621
636
  self.session_is_guest = false
622
637
  self.sequence_counter = 0
@@ -13,6 +13,19 @@ module RubySMB
13
13
  # Raised when an invalid packet is received
14
14
  class InvalidPacket < DcerpcError; end
15
15
 
16
+ # Raised when a fault response is received
17
+ class FaultError < InvalidPacket
18
+ attr_reader :status_code
19
+ def initialize(message=nil, status:)
20
+ @status_code = status
21
+ super(message)
22
+ end
23
+
24
+ def status_name
25
+ RubySMB::Dcerpc::Fault::Status.name(@status_code)
26
+ end
27
+ end
28
+
16
29
  # Raised when an error is returned during a Winreg operation
17
30
  class WinregError < DcerpcError; end
18
31
 
@@ -0,0 +1,83 @@
1
+ module RubySMB::Dcerpc::Fault
2
+ module Status
3
+ # DCERPC
4
+ NCA_S_FAULT_OTHER = 0x00000001
5
+ NCA_S_FAULT_ACCESS_DENIED = 0x00000005
6
+ NCA_S_FAULT_NDR = 0x000006F7
7
+ NCA_S_FAULT_CANT_PERFORM = 0x000006D8
8
+ NCA_S_FAULT_INT_DIV_BY_ZERO = 0x1C000001
9
+ NCA_S_FAULT_ADDR_ERROR = 0x1C000002
10
+ NCA_S_FAULT_FP_DIV_ZERO = 0x1C000003
11
+ NCA_S_FAULT_FP_UNDERFLOW = 0x1C000004
12
+ NCA_S_FAULT_FP_OVERFLOW = 0x1C000005
13
+ NCA_S_FAULT_INVALID_TAG = 0x1C000006
14
+ NCA_S_FAULT_INVALID_BOUND = 0x1C000007
15
+ NCA_RPC_VERSION_MISMATCH = 0x1C000008
16
+ NCA_UNSPEC_REJECT = 0x1C000009
17
+ NCA_S_BAD_ACTID = 0x1C00000A
18
+ NCA_WHO_ARE_YOU_FAILED = 0x1C00000B
19
+ NCA_MANAGER_NOT_ENTERED = 0x1C00000C
20
+ NCA_S_FAULT_CANCEL = 0x1C00000D
21
+ NCA_S_FAULT_ILL_INST = 0x1C00000E
22
+ NCA_S_FAULT_FP_ERROR = 0x1C00000F
23
+ NCA_S_FAULT_INT_OVERFLOW = 0x1C000010
24
+ NCA_S_FAULT_PIPE_EMPTY = 0x1C000014
25
+ NCA_S_FAULT_PIPE_CLOSED = 0x1C000015
26
+ NCA_S_FAULT_PIPE_ORDER = 0x1C000016
27
+ NCA_S_FAULT_PIPE_DISCIPLINE = 0x1C000017
28
+ NCA_S_FAULT_PIPE_COMM_ERROR = 0x1C000018
29
+ NCA_S_FAULT_PIPE_MEMORY = 0x1C000019
30
+ NCA_S_FAULT_CONTEXT_MISMATCH = 0x1C00001A
31
+ NCA_S_FAULT_REMOTE_NO_MEMORY = 0x1C00001B
32
+ NCA_INVALID_PRES_CONTEXT_ID = 0x1C00001C
33
+ NCA_UNSUPPORTED_AUTHN_LEVEL = 0x1C00001D
34
+ NCA_INVALID_CHECKSUM = 0x1C00001F
35
+ NCA_INVALID_CRC = 0x1C000020
36
+ NCS_S_FAULT_USER_DEFINED = 0x1C000021
37
+ NCA_S_FAULT_TX_OPEN_FAILED = 0x1C000022
38
+ NCA_S_FAULT_CODESET_CONV_ERROR = 0x1C000023
39
+ NCA_S_FAULT_OBJECT_NOT_FOUND = 0x1C000024
40
+ NCA_S_FAULT_NO_CLIENT_STUB = 0x1C000025
41
+ NCA_OP_RNG_ERROR = 0x1C010002
42
+ NCA_UNK_IF = 0x1C010003
43
+ NCA_WRONG_BOOT_TIME = 0x1C010006
44
+ NCA_S_YOU_CRASHED = 0x1C010009
45
+ NCA_PROTO_ERROR = 0x1C01000B
46
+ NCA_OUT_ARGS_TOO_BIG = 0x1C010013
47
+ NCA_SERVER_TOO_BUSY = 0x1C010014
48
+ NCA_UNSUPPORTED_TYPE = 0x1C010017
49
+ # Microsoft specific codes
50
+ E_NOTIMPL = 0x80004001
51
+ E_POINTER = 0x80004003
52
+ E_AOBRT = 0x80004004
53
+ E_UNEXPECTED = 0x8000FFFF
54
+ RPC_E_SERVERFAULT = 0x80010105
55
+ RPC_E_DISCONNECTED = 0x80010108
56
+ RPC_E_INVALID_IPID = 0x80010113
57
+ RPC_E_TIMEOUT = 0x8001011F
58
+ DISP_E_MEMBERNOTFOUND = 0x80020003
59
+ DISP_E_UNKNOWNNAME = 0x80020006
60
+ DISP_E_BADPARAMCOUNT = 0x8002000E
61
+ CBA_E_MALFORMED = 0x8004CB00
62
+ CBA_E_UNKNOWNOBJECT = 0x8004CB01
63
+ CBA_E_INVALIDID = 0x8004CB05
64
+ CBA_E_INVALIDCOOKIE = 0x8004CB09
65
+ CBA_E_QOSTYPEUNSUPPORTED = 0x8004CB0B
66
+ CBA_E_QOSVALUEUNSUPPORTED = 0x8004CB0C
67
+ CBA_E_NOTAPPLICABLE = 0x8004CB0F
68
+ CBA_E_LIMITVIOLATION = 0x8004CB12
69
+ CBA_E_QOSTYPENOTAPPLICABLE = 0x8004CB13
70
+ CBA_E_OUTOFPARTNERACCOS = 0x8004CB18
71
+ CBA_E_FLAGUNSUPPORTED = 0x8004CB1C
72
+ CBA_E_FRAMECOUNTUNSUPPORTED = 0x8004CB23
73
+ CBA_E_MODECHANGE = 0x8004CB25
74
+ E_OUTOFMEMORY = 0x8007000E
75
+ E_INVALIDARG = 0x80070057
76
+ RPC_S_PROCNUM_OUT_OF_RANGE = 0x800706D1
77
+ OR_INVALID_OXID = 0x80070776
78
+
79
+ def self.name(value)
80
+ constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
81
+ end
82
+ end
83
+ end
@@ -312,29 +312,31 @@ module RubySMB::Dcerpc::Ndr
312
312
  def initialize_instance
313
313
  @read_until_index = 0
314
314
  @max_count = 0
315
+ @max_count_set = false
315
316
  super
316
317
  end
317
318
 
318
319
  def insert(index, *objs)
319
320
  obj = super
320
- @max_count = length
321
+ @max_count = length unless @max_count_set
321
322
  obj
322
323
  end
323
324
 
324
325
  def slice_index(index)
325
326
  obj = super
326
- @max_count = length
327
+ @max_count = length unless @max_count_set
327
328
  obj
328
329
  end
329
330
 
330
331
  def []=(index, value)
331
332
  obj = super
332
- @max_count = length
333
+ @max_count = length unless @max_count_set
333
334
  obj
334
335
  end
335
336
 
336
337
  def set_max_count(val)
337
338
  @max_count = @read_until_index = val
339
+ @max_count_set = true
338
340
  end
339
341
  end
340
342
 
@@ -650,7 +652,7 @@ module RubySMB::Dcerpc::Ndr
650
652
  include ConstructedTypePlugin
651
653
 
652
654
  def should_process_max_count?
653
- # According to the NDR defintion for Structures Containing a Conformant
655
+ # According to the NDR definition for Structures Containing a Conformant
654
656
  # Array:
655
657
  #
656
658
  # "In the NDR representation of a structure that contains a
@@ -761,13 +763,13 @@ module RubySMB::Dcerpc::Ndr
761
763
  return (4 - (rel_offset % 4)) % 4
762
764
  end
763
765
  if obj.is_a?(ConfPlugin)
764
- # `max_count` should have been handled at the begining of the structure
766
+ # `max_count` should have been handled at the beginning of the structure
765
767
  # already. We need to fix `rel_offset` since it includes the
766
768
  # `max_count` 4 bytes, plus the possible padding bytes needed to align
767
769
  # the structure. This is required because BinData Struct is not
768
- # aware of `max_count` and considere the first field to be the begining
770
+ # aware of `max_count` and consider the first field to be the beginning
769
771
  # of the structure instead. We have to make sure the alignment is
770
- # calculated from the begining of the structure.
772
+ # calculated from the beginning of the structure.
771
773
  align = eval_parameter(:byte_align)
772
774
  pad_length = (align - (4 % align)) % align
773
775
  rel_offset += (4 + pad_length)
@@ -776,7 +778,7 @@ module RubySMB::Dcerpc::Ndr
776
778
  # (not Varying). The size information (max_count) has been place in
777
779
  # from of the structure and no other size information is present before
778
780
  # the actual elements of the array. Therefore, the alignment must be
779
- # done accroding to th rules of the elements. Since a NdrArray has its
781
+ # done according to the rules of the elements. Since a NdrArray has its
780
782
  # default :byte_align value set to 4 (:max_count size), we have to make
781
783
  # sure the element size is used instead.
782
784
  unless obj.is_a?(VarPlugin)
@@ -838,6 +840,10 @@ module RubySMB::Dcerpc::Ndr
838
840
  end
839
841
  end
840
842
 
843
+ #
844
+ # [Unions](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_08)
845
+ #
846
+
841
847
  # TODO: Unions
842
848
  # TODO: Pipes
843
849
 
@@ -1207,6 +1213,11 @@ module RubySMB::Dcerpc::Ndr
1207
1213
  extend PointerClassPlugin
1208
1214
  end
1209
1215
 
1216
+ class NdrUint16ArrayPtr < NdrConfVarArray
1217
+ default_parameters type: :ndr_uint16
1218
+ extend PointerClassPlugin
1219
+ end
1220
+
1210
1221
  class NdrFileTimePtr < NdrFileTime
1211
1222
  extend PointerClassPlugin
1212
1223
  end
@@ -60,16 +60,21 @@ module RubySMB
60
60
  string :default
61
61
  end
62
62
  choice 'Samr', selection: -> { opnum } do
63
- samr_connect_request Samr::SAMR_CONNECT
64
- samr_lookup_domain_in_sam_server_request Samr::SAMR_LOOKUP_DOMAIN_IN_SAM_SERVER
65
- samr_open_domain_request Samr::SAMR_OPEN_DOMAIN
66
- samr_enumerate_users_in_domain_request Samr::SAMR_ENUMERATE_USERS_IN_DOMAIN
67
- samr_rid_to_sid_request Samr::SAMR_RID_TO_SID
68
- samr_close_handle_request Samr::SAMR_CLOSE_HANDLE
69
- samr_get_alias_membership_request Samr::SAMR_GET_ALIAS_MEMBERSHIP
70
- samr_open_user_request Samr::SAMR_OPEN_USER
71
- samr_get_groups_for_user_request Samr::SAMR_GET_GROUPS_FOR_USER
72
- string :default
63
+ samr_connect_request Samr::SAMR_CONNECT
64
+ samr_lookup_domain_in_sam_server_request Samr::SAMR_LOOKUP_DOMAIN_IN_SAM_SERVER
65
+ samr_open_domain_request Samr::SAMR_OPEN_DOMAIN
66
+ samr_enumerate_users_in_domain_request Samr::SAMR_ENUMERATE_USERS_IN_DOMAIN
67
+ samr_rid_to_sid_request Samr::SAMR_RID_TO_SID
68
+ samr_close_handle_request Samr::SAMR_CLOSE_HANDLE
69
+ samr_get_alias_membership_request Samr::SAMR_GET_ALIAS_MEMBERSHIP
70
+ samr_open_user_request Samr::SAMR_OPEN_USER
71
+ samr_get_groups_for_user_request Samr::SAMR_GET_GROUPS_FOR_USER
72
+ samr_enumerate_domains_in_sam_server_request Samr::SAMR_ENUMERATE_DOMAINS_IN_SAM_SERVER
73
+ samr_lookup_names_in_domain_request Samr::SAMR_LOOKUP_NAMES_IN_DOMAIN
74
+ samr_create_user2_in_domain_request Samr::SAMR_CREATE_USER2_IN_DOMAIN
75
+ samr_set_information_user2_request Samr::SAMR_SET_INFORMATION_USER2
76
+ samr_delete_user_request Samr::SAMR_DELETE_USER
77
+ string :default
73
78
  end
74
79
  choice 'Wkssvc', selection: -> { opnum } do
75
80
  netr_wksta_get_info_request Wkssvc::NETR_WKSTA_GET_INFO
@@ -109,6 +109,11 @@ module RubySMB
109
109
  end
110
110
  end
111
111
 
112
+ class RpcUnicodeStringConfVarArray < Ndr::NdrConfVarArray
113
+ extend Ndr::ArrayClassPlugin
114
+ default_parameters type: :rpc_unicode_string
115
+ end
116
+
112
117
  # A pointer to a RPC_UNICODE_STRING structure
113
118
  class PrpcUnicodeString < RpcUnicodeString
114
119
  extend Ndr::PointerClassPlugin
@@ -0,0 +1,24 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Samr
4
+
5
+ # [3.1.5.4.4 SamrCreateUser2InDomain (Opnum 50)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/a98d7fbb-1735-4fbf-b41a-ef363c899002)
6
+ class SamrCreateUser2InDomainRequest < BinData::Record
7
+ attr_reader :opnum
8
+
9
+ endian :little
10
+
11
+ sampr_handle :domain_handle
12
+ rpc_unicode_string :name
13
+ ndr_uint32 :account_type
14
+ ndr_uint32 :desired_access
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = SAMR_CREATE_USER2_IN_DOMAIN
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Samr
4
+
5
+ # [3.1.5.4.4 SamrCreateUser2InDomain (Opnum 50)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/a98d7fbb-1735-4fbf-b41a-ef363c899002)
6
+ class SamrCreateUser2InDomainResponse < BinData::Record
7
+ attr_reader :opnum
8
+
9
+ endian :little
10
+
11
+ sampr_handle :user_handle
12
+ ndr_uint32 :granted_access
13
+ ndr_uint32 :relative_id
14
+ ndr_uint32 :error_status
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = SAMR_CREATE_USER2_IN_DOMAIN
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end