ruby_smb 3.3.6 → 3.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +14 -0
  4. data/examples/registry_key_security_descriptor.rb +109 -0
  5. data/lib/ruby_smb/client/winreg.rb +12 -0
  6. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  7. data/lib/ruby_smb/dcerpc/lsarpc/lsar_close_handle_request.rb +22 -0
  8. data/lib/ruby_smb/dcerpc/lsarpc/lsar_close_handle_response.rb +23 -0
  9. data/lib/ruby_smb/dcerpc/lsarpc/lsar_lookup_sids_request.rb +26 -0
  10. data/lib/ruby_smb/dcerpc/lsarpc/lsar_lookup_sids_response.rb +25 -0
  11. data/lib/ruby_smb/dcerpc/lsarpc/lsar_open_policy2_request.rb +24 -0
  12. data/lib/ruby_smb/dcerpc/lsarpc/lsar_open_policy2_response.rb +23 -0
  13. data/lib/ruby_smb/dcerpc/lsarpc/lsar_open_policy_request.rb +24 -0
  14. data/lib/ruby_smb/dcerpc/lsarpc/lsar_open_policy_response.rb +23 -0
  15. data/lib/ruby_smb/dcerpc/lsarpc/lsar_query_information_policy2_request.rb +23 -0
  16. data/lib/ruby_smb/dcerpc/lsarpc/lsar_query_information_policy2_response.rb +23 -0
  17. data/lib/ruby_smb/dcerpc/lsarpc/lsar_query_information_policy_request.rb +23 -0
  18. data/lib/ruby_smb/dcerpc/lsarpc/lsar_query_information_policy_response.rb +23 -0
  19. data/lib/ruby_smb/dcerpc/lsarpc.rb +634 -2
  20. data/lib/ruby_smb/dcerpc/ndr.rb +10 -4
  21. data/lib/ruby_smb/dcerpc/request.rb +26 -16
  22. data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +1 -1
  23. data/lib/ruby_smb/dcerpc/samr/rpc_sid.rb +1 -1
  24. data/lib/ruby_smb/dcerpc/winreg/get_key_security_request.rb +26 -0
  25. data/lib/ruby_smb/dcerpc/winreg/get_key_security_response.rb +26 -0
  26. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +2 -0
  27. data/lib/ruby_smb/dcerpc/winreg/set_key_security_request.rb +26 -0
  28. data/lib/ruby_smb/dcerpc/winreg/set_key_security_response.rb +25 -0
  29. data/lib/ruby_smb/dcerpc/winreg.rb +121 -9
  30. data/lib/ruby_smb/field/security_descriptor.rb +17 -0
  31. data/lib/ruby_smb/version.rb +1 -1
  32. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_close_handle_request_spec.rb +40 -0
  33. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_close_handle_response_spec.rb +46 -0
  34. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_lookup_sids_request_spec.rb +69 -0
  35. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_lookup_sids_response_spec.rb +56 -0
  36. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_open_policy2_request_spec.rb +68 -0
  37. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_open_policy2_response_spec.rb +46 -0
  38. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_open_policy_request_spec.rb +68 -0
  39. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_open_policy_response_spec.rb +45 -0
  40. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_query_information_policy2_request_spec.rb +47 -0
  41. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_query_information_policy2_response_spec.rb +54 -0
  42. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_query_information_policy_request_spec.rb +46 -0
  43. data/spec/lib/ruby_smb/dcerpc/lsarpc/lsar_query_information_policy_response_spec.rb +53 -0
  44. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +80 -0
  45. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +267 -18
  46. data.tar.gz.sig +0 -0
  47. metadata +44 -3
  48. metadata.gz.sig +0 -0
@@ -18,22 +18,24 @@ module RubySMB
18
18
  choice :stub, label: 'Stub', selection: -> { @obj.parent.get_parameter(:endpoint) || '' } do
19
19
  string 'Encrypted'
20
20
  choice 'Winreg', selection: -> { opnum } do
21
- open_root_key_request Winreg::OPEN_HKCR, opnum: Winreg::OPEN_HKCR
22
- open_root_key_request Winreg::OPEN_HKCU, opnum: Winreg::OPEN_HKCU
23
- open_root_key_request Winreg::OPEN_HKLM, opnum: Winreg::OPEN_HKLM
24
- open_root_key_request Winreg::OPEN_HKPD, opnum: Winreg::OPEN_HKPD
25
- open_root_key_request Winreg::OPEN_HKU, opnum: Winreg::OPEN_HKU
26
- open_root_key_request Winreg::OPEN_HKCC, opnum: Winreg::OPEN_HKCC
27
- open_root_key_request Winreg::OPEN_HKPT, opnum: Winreg::OPEN_HKPT
28
- open_root_key_request Winreg::OPEN_HKPN, opnum: Winreg::OPEN_HKPN
29
- close_key_request Winreg::REG_CLOSE_KEY
30
- enum_key_request Winreg::REG_ENUM_KEY
31
- enum_value_request Winreg::REG_ENUM_VALUE
32
- open_key_request Winreg::REG_OPEN_KEY
33
- query_info_key_request Winreg::REG_QUERY_INFO_KEY
34
- query_value_request Winreg::REG_QUERY_VALUE
35
- create_key_request Winreg::REG_CREATE_KEY
36
- save_key_request Winreg::REG_SAVE_KEY
21
+ open_root_key_request Winreg::OPEN_HKCR, opnum: Winreg::OPEN_HKCR
22
+ open_root_key_request Winreg::OPEN_HKCU, opnum: Winreg::OPEN_HKCU
23
+ open_root_key_request Winreg::OPEN_HKLM, opnum: Winreg::OPEN_HKLM
24
+ open_root_key_request Winreg::OPEN_HKPD, opnum: Winreg::OPEN_HKPD
25
+ open_root_key_request Winreg::OPEN_HKU, opnum: Winreg::OPEN_HKU
26
+ open_root_key_request Winreg::OPEN_HKCC, opnum: Winreg::OPEN_HKCC
27
+ open_root_key_request Winreg::OPEN_HKPT, opnum: Winreg::OPEN_HKPT
28
+ open_root_key_request Winreg::OPEN_HKPN, opnum: Winreg::OPEN_HKPN
29
+ close_key_request Winreg::REG_CLOSE_KEY
30
+ enum_key_request Winreg::REG_ENUM_KEY
31
+ enum_value_request Winreg::REG_ENUM_VALUE
32
+ open_key_request Winreg::REG_OPEN_KEY
33
+ query_info_key_request Winreg::REG_QUERY_INFO_KEY
34
+ query_value_request Winreg::REG_QUERY_VALUE
35
+ create_key_request Winreg::REG_CREATE_KEY
36
+ save_key_request Winreg::REG_SAVE_KEY
37
+ get_key_security_request Winreg::REG_GET_KEY_SECURITY
38
+ set_key_security_request Winreg::REG_SET_KEY_SECURITY
37
39
  string :default
38
40
  end
39
41
  choice 'Netlogon', selection: -> { opnum } do
@@ -109,6 +111,14 @@ module RubySMB
109
111
  efs_rpc_query_recovery_agents_request Efsrpc::EFS_RPC_QUERY_RECOVERY_AGENTS
110
112
  efs_rpc_query_users_on_file_request Efsrpc::EFS_RPC_QUERY_USERS_ON_FILE
111
113
  end
114
+ choice 'Lsarpc', selection: -> { opnum } do
115
+ lsar_open_policy_request Lsarpc::LSAR_OPEN_POLICY
116
+ lsar_open_policy2_request Lsarpc::LSAR_OPEN_POLICY2
117
+ lsar_query_information_policy_request Lsarpc::LSAR_QUERY_INFORMATION_POLICY
118
+ lsar_query_information_policy2_request Lsarpc::LSAR_QUERY_INFORMATION_POLICY2
119
+ lsar_close_handle_request Lsarpc::LSAR_CLOSE_HANDLE
120
+ lsar_lookup_sids_request Lsarpc::LSAR_LOOKUP_SIDS
121
+ end
112
122
  string :default
113
123
  end
114
124
 
@@ -20,7 +20,7 @@ module RubySMB
20
20
  when BinData::Stringz, BinData::String, String
21
21
  self.buffer = val.to_s
22
22
  val_length = val.strip.length
23
- val_length += 1 unless val == ''
23
+ val_length += 1
24
24
  self.buffer_length = val_length * 2
25
25
  self.maximum_length = val_length * 2
26
26
  else
@@ -107,7 +107,7 @@ module RubySMB
107
107
  case val
108
108
  when String
109
109
  elems = val.split('-')
110
- raise ArgumentError, "Wrong SID format" unless elems[0].downcase == 's'
110
+ raise ArgumentError, "Wrong SID format for #{val.inspect}" unless elems[0].downcase == 's'
111
111
  self.revision = elems[1].to_i
112
112
  self.sub_authority_count = elems[3..-1].size
113
113
  self.identifier_authority = [0, 0, 0, 0, 0, elems[2].to_i]
@@ -0,0 +1,26 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Winreg
4
+
5
+ # This class represents a GetKeySecurity Request Packet as defined in
6
+ # [3.1.5.13 BaseRegGetKeySecurity (Opnum 12)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/b0e1868c-f4fd-4b43-959f-c0f0cac3ee26)
7
+ class GetKeySecurityRequest < BinData::Record
8
+ attr_reader :opnum
9
+
10
+ endian :little
11
+
12
+ rpc_hkey :hkey
13
+ uint32 :security_information
14
+ rpc_security_descriptor :prpc_security_descriptor_in
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = REG_GET_KEY_SECURITY
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+
26
+
@@ -0,0 +1,26 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Winreg
4
+
5
+ # This class represents a GetKeySecurity Response Packet as defined in
6
+ # [3.1.5.13 BaseRegGetKeySecurity (Opnum 12)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/b0e1868c-f4fd-4b43-959f-c0f0cac3ee26)
7
+ class GetKeySecurityResponse < BinData::Record
8
+ attr_reader :opnum
9
+
10
+ endian :little
11
+
12
+ rpc_security_descriptor :prpc_security_descriptor_out
13
+ ndr_uint32 :error_status
14
+
15
+ def initialize_instance
16
+ super
17
+ @opnum = REG_GET_KEY_SECURITY
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+
25
+
26
+
@@ -25,6 +25,8 @@ module RubySMB
25
25
  def data
26
26
  bytes = lp_data.to_a.pack('C*')
27
27
  case lp_type
28
+ when 0 # 0 is undefined type, let's consider an array of bytes
29
+ bytes
28
30
  when 1,2
29
31
  bytes.force_encoding('utf-16le').strip
30
32
  when 3
@@ -0,0 +1,26 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Winreg
4
+
5
+ # This class represents a SetKeySecurity Request Packet as defined in
6
+ # [3.1.5.21 BaseRegSetKeySecurity (Opnum 21)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/da18856c-8a6d-4217-8e93-3625865e562c)
7
+ class SetKeySecurityRequest < BinData::Record
8
+ attr_reader :opnum
9
+
10
+ endian :little
11
+
12
+ rpc_hkey :hkey
13
+ uint32 :security_information
14
+ rpc_security_descriptor :prpc_security_descriptor
15
+
16
+ def initialize_instance
17
+ super
18
+ @opnum = REG_SET_KEY_SECURITY
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+
26
+
@@ -0,0 +1,25 @@
1
+ module RubySMB
2
+ module Dcerpc
3
+ module Winreg
4
+
5
+ # This class represents a SetKeySecurity Response Packet as defined in
6
+ # [3.1.5.21 BaseRegSetKeySecurity (Opnum 21)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/da18856c-8a6d-4217-8e93-3625865e562c)
7
+ class SetKeySecurityResponse < BinData::Record
8
+ attr_reader :opnum
9
+
10
+ endian :little
11
+
12
+ ndr_uint32 :error_status
13
+
14
+ def initialize_instance
15
+ super
16
+ @opnum = REG_SET_KEY_SECURITY
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+
24
+
25
+
@@ -16,10 +16,12 @@ module RubySMB
16
16
  REG_CREATE_KEY = 0x06
17
17
  REG_ENUM_KEY = 0x09
18
18
  REG_ENUM_VALUE = 0x0a
19
+ REG_GET_KEY_SECURITY = 0x0c
19
20
  REG_OPEN_KEY = 0x0f
20
21
  REG_QUERY_INFO_KEY = 0x10
21
22
  REG_QUERY_VALUE = 0x11
22
23
  REG_SAVE_KEY = 0x14
24
+ REG_SET_KEY_SECURITY = 0x15
23
25
  OPEN_HKCC = 0x1b
24
26
  OPEN_HKPT = 0x20
25
27
  OPEN_HKPN = 0x21
@@ -43,6 +45,10 @@ module RubySMB
43
45
  require 'ruby_smb/dcerpc/winreg/create_key_response'
44
46
  require 'ruby_smb/dcerpc/winreg/save_key_request'
45
47
  require 'ruby_smb/dcerpc/winreg/save_key_response'
48
+ require 'ruby_smb/dcerpc/winreg/get_key_security_request'
49
+ require 'ruby_smb/dcerpc/winreg/get_key_security_response'
50
+ require 'ruby_smb/dcerpc/winreg/set_key_security_request'
51
+ require 'ruby_smb/dcerpc/winreg/set_key_security_response'
46
52
 
47
53
  ROOT_KEY_MAP = {
48
54
  "HKEY_CLASSES_ROOT" => OPEN_HKCR,
@@ -65,6 +71,8 @@ module RubySMB
65
71
 
66
72
  BUFFER_SIZE = 1024
67
73
 
74
+ RegValue = Struct.new(:type, :data)
75
+
68
76
  # Open the registry root key and return a handle for it. The key can be
69
77
  # either a long format (e.g. HKEY_LOCAL_MACHINE) or a short format
70
78
  # (e.g. HKLM)
@@ -105,10 +113,7 @@ module RubySMB
105
113
  # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
106
114
  def open_key(handle, sub_key)
107
115
  openkey_request_packet = RubySMB::Dcerpc::Winreg::OpenKeyRequest.new(hkey: handle, lp_sub_key: sub_key)
108
- openkey_request_packet.sam_desired.read_control = 1
109
- openkey_request_packet.sam_desired.key_query_value = 1
110
- openkey_request_packet.sam_desired.key_enumerate_sub_keys = 1
111
- openkey_request_packet.sam_desired.key_notify = 1
116
+ openkey_request_packet.sam_desired.maximum_allowed = 1
112
117
  response = dcerpc_request(openkey_request_packet)
113
118
  begin
114
119
  open_key_response = RubySMB::Dcerpc::Winreg::OpenKeyResponse.read(response)
@@ -124,11 +129,12 @@ module RubySMB
124
129
  end
125
130
 
126
131
  # Retrieve the data associated with the named value of a specified
127
- # registry open key.
132
+ # registry open key. This will also return the type if required.
128
133
  #
129
134
  # @param handle [Ndr::NdrContextHandle] the handle for the key
130
135
  # @param value_name [String] the name of the value
131
- # @return [String] the data of the value entry
136
+ # @param value_name [Boolean] also return the data type if set to true
137
+ # @return [RegValue] a RegValue struct containing the data type and the actual data of the value entry
132
138
  # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a QueryValueResponse packet
133
139
  # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
134
140
  def query_value(handle, value_name)
@@ -161,7 +167,7 @@ module RubySMB
161
167
  "#{WindowsError::Win32.find_by_retval(query_value_response.error_status.value).join(',')}"
162
168
  end
163
169
 
164
- query_value_response.data
170
+ RegValue.new(query_value_response.lp_type, query_value_response.data)
165
171
  end
166
172
 
167
173
  # Close the handle to the registry key.
@@ -323,6 +329,7 @@ module RubySMB
323
329
  # exists, false otherwise.
324
330
  #
325
331
  # @param key [String] the registry key to check
332
+ # @param bind [Boolean] Bind to the winreg endpoint if true (default)
326
333
  # @return [Boolean]
327
334
  def has_registry_key?(key, bind: true)
328
335
  bind(endpoint: RubySMB::Dcerpc::Winreg) if bind
@@ -345,6 +352,7 @@ module RubySMB
345
352
  #
346
353
  # @param key [String] the registry key
347
354
  # @param value_name [String] the name of the value to read
355
+ # @param bind [Boolean] Bind to the winreg endpoint if true (default)
348
356
  # @return [String] the data of the value entry
349
357
  def read_registry_key_value(key, value_name, bind: true)
350
358
  bind(endpoint: RubySMB::Dcerpc::Winreg) if bind
@@ -352,8 +360,8 @@ module RubySMB
352
360
  root_key, sub_key = key.gsub(/\//, '\\').split('\\', 2)
353
361
  root_key_handle = open_root_key(root_key)
354
362
  subkey_handle = open_key(root_key_handle, sub_key)
355
- value = query_value(subkey_handle, value_name)
356
- value
363
+ reg_value = query_value(subkey_handle, value_name)
364
+ reg_value.data
357
365
  ensure
358
366
  close_key(subkey_handle) if subkey_handle
359
367
  close_key(root_key_handle) if root_key_handle
@@ -363,6 +371,7 @@ module RubySMB
363
371
  # is provided, it enumerates its subkeys.
364
372
  #
365
373
  # @param key [String] the registry key
374
+ # @param bind [Boolean] Bind to the winreg endpoint if true (default)
366
375
  # @return [Array<String>] the subkeys
367
376
  def enum_registry_key(key, bind: true)
368
377
  bind(endpoint: RubySMB::Dcerpc::Winreg) if bind
@@ -389,6 +398,7 @@ module RubySMB
389
398
  # Enumerate the values for the specified registry key.
390
399
  #
391
400
  # @param key [String] the registry key
401
+ # @param bind [Boolean] Bind to the winreg endpoint if true (default)
392
402
  # @return [Array<String>] the values
393
403
  def enum_registry_values(key, bind: true)
394
404
  bind(endpoint: RubySMB::Dcerpc::Winreg) if bind
@@ -412,6 +422,108 @@ module RubySMB
412
422
  close_key(root_key_handle) if root_key_handle && root_key_handle != subkey_handle
413
423
  end
414
424
 
425
+
426
+ # Retrieve the security descriptor for the given registry key handle.
427
+ #
428
+ # @param handle [String] the handle to the registry key
429
+ # @param security_information [] the security information to query (see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/23e75ca3-98fd-4396-84e5-86cd9d40d343). These constants are defined in the `RubySMB::Field::SecurityDescriptor` class
430
+ # @return [String] The security descriptor as a byte stream
431
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a GetKeySecurityResponse packet
432
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
433
+ def get_key_security(handle, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION)
434
+ get_key_security_request = RubySMB::Dcerpc::Winreg::GetKeySecurityRequest.new(
435
+ hkey: handle,
436
+ security_information: security_information,
437
+ prpc_security_descriptor_in: { cb_in_security_descriptor: 4096 }
438
+ )
439
+ response = dcerpc_request(get_key_security_request)
440
+ begin
441
+ get_key_security_response = RubySMB::Dcerpc::Winreg::GetKeySecurityResponse.read(response)
442
+ rescue IOError
443
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the GetKeySecurity response"
444
+ end
445
+ unless get_key_security_response.error_status == WindowsError::Win32::ERROR_SUCCESS
446
+ raise RubySMB::Dcerpc::Error::WinregError, "Error returned when querying information: "\
447
+ "#{WindowsError::Win32.find_by_retval(get_key_security_response.error_status.value).join(',')}"
448
+ end
449
+
450
+ get_key_security_response.prpc_security_descriptor_out.lp_security_descriptor.to_a.pack('C*')
451
+ end
452
+
453
+ # Retrieve the security descriptor for the given key.
454
+ #
455
+ # @param key [String] the registry key
456
+ # @param security_information [] the security information to query (see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/23e75ca3-98fd-4396-84e5-86cd9d40d343). These constants are defined in the `RubySMB::Field::SecurityDescriptor` class
457
+ # @param bind [Boolean] Bind to the winreg endpoint if true (default)
458
+ # @return [String] The security descriptor as a byte stream
459
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a GetKeySecurityResponse packet
460
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
461
+ def get_key_security_descriptor(key, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION, bind: true)
462
+ bind(endpoint: RubySMB::Dcerpc::Winreg) if bind
463
+
464
+ root_key, sub_key = key.gsub(/\//, '\\').split('\\', 2)
465
+ root_key_handle = open_root_key(root_key)
466
+ subkey_handle = open_key(root_key_handle, sub_key)
467
+ get_key_security(subkey_handle, security_information)
468
+ ensure
469
+ close_key(subkey_handle) if subkey_handle
470
+ close_key(root_key_handle) if root_key_handle
471
+ end
472
+
473
+ # Set the security descriptor for the given registry key handle.
474
+ #
475
+ # @param handle [String] the handle to the registry key
476
+ # @param security_descriptor [String] the new security descriptor to set as a byte stream
477
+ # @param security_information [] the security information to query (see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/23e75ca3-98fd-4396-84e5-86cd9d40d343). These constants are defined in the `RubySMB::Field::SecurityDescriptor` class
478
+ # @param bind [Boolean] Bind to the winreg endpoint if true (default)
479
+ # @return [Integer] The error status returned by the DCERPC call
480
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a SetKeySecurityResponse packet
481
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
482
+ def set_key_security(handle, security_descriptor, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION)
483
+ set_key_security_request = RubySMB::Dcerpc::Winreg::SetKeySecurityRequest.new(
484
+ hkey: handle,
485
+ security_information: security_information,
486
+ prpc_security_descriptor: {
487
+ lp_security_descriptor: security_descriptor.bytes,
488
+ cb_in_security_descriptor: security_descriptor.b.size,
489
+ cb_out_security_descriptor: security_descriptor.b.size
490
+ }
491
+ )
492
+ response = dcerpc_request(set_key_security_request)
493
+ begin
494
+ set_key_security_response = RubySMB::Dcerpc::Winreg::SetKeySecurityResponse.read(response)
495
+ rescue IOError
496
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the SetKeySecurity response"
497
+ end
498
+ unless set_key_security_response.error_status == WindowsError::Win32::ERROR_SUCCESS
499
+ raise RubySMB::Dcerpc::Error::WinregError, "Error returned when setting the registry key: "\
500
+ "#{WindowsError::Win32.find_by_retval(set_key_security_response.error_status.value).join(',')}"
501
+ end
502
+
503
+ set_key_security_response.error_status
504
+ end
505
+
506
+ # Set the security descriptor for the given key.
507
+ #
508
+ # @param key [String] the registry key
509
+ # @param security_descriptor [String] the new security descriptor to set as a byte stream
510
+ # @param security_information [] the security information to query (see https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/23e75ca3-98fd-4396-84e5-86cd9d40d343). These constants are defined in the `RubySMB::Field::SecurityDescriptor` class
511
+ # @param bind [Boolean] Bind to the winreg endpoint if true (default)
512
+ # @return [Integer] The error status returned by the DCERPC call
513
+ # @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a SetKeySecurityResponse packet
514
+ # @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
515
+ def set_key_security_descriptor(key, security_descriptor, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION, bind: true)
516
+ bind(endpoint: RubySMB::Dcerpc::Winreg) if bind
517
+
518
+ root_key, sub_key = key.gsub(/\//, '\\').split('\\', 2)
519
+ root_key_handle = open_root_key(root_key)
520
+ subkey_handle = open_key(root_key_handle, sub_key)
521
+ set_key_security(subkey_handle, security_descriptor, security_information)
522
+ ensure
523
+ close_key(subkey_handle) if subkey_handle
524
+ close_key(root_key_handle) if root_key_handle
525
+ end
526
+
415
527
  end
416
528
  end
417
529
  end
@@ -3,6 +3,23 @@ module RubySMB
3
3
  # Class representing a SECURITY_DESCRIPTOR as defined in
4
4
  # [2.4.6 SECURITY_DESCRIPTOR](https://msdn.microsoft.com/en-us/library/cc230366.aspx)
5
5
  class SecurityDescriptor < BinData::Record
6
+
7
+ # Security Information as defined in
8
+ # [2.4.7 SECURITY_INFORMATION](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/23e75ca3-98fd-4396-84e5-86cd9d40d343)
9
+ OWNER_SECURITY_INFORMATION = 0x00000001
10
+ GROUP_SECURITY_INFORMATION = 0x00000002
11
+ DACL_SECURITY_INFORMATION = 0x00000004
12
+ SACL_SECURITY_INFORMATION = 0x00000008
13
+ LABEL_SECURITY_INFORMATION = 0x00000010
14
+ UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000
15
+ UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000
16
+ PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000
17
+ PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
18
+ ATTRIBUTE_SECURITY_INFORMATION = 0x00000020
19
+ SCOPE_SECURITY_INFORMATION = 0x00000040
20
+ PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080
21
+ BACKUP_SECURITY_INFORMATION = 0x00010000
22
+
6
23
  endian :little
7
24
  uint8 :revision, label: 'Revision', initial_value: 0x01
8
25
  uint8 :sbz1, label: 'Resource Manager Control Bits'
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.3.6'.freeze
2
+ VERSION = '3.3.8'.freeze
3
3
  end
@@ -0,0 +1,40 @@
1
+ RSpec.describe RubySMB::Dcerpc::Lsarpc::LsarCloseHandleRequest do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :policy_handle }
5
+ it { is_expected.to respond_to :opnum }
6
+
7
+ it 'is little endian' do
8
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
9
+ end
10
+ it 'is a BinData::Record' do
11
+ expect(packet).to be_a(BinData::Record)
12
+ end
13
+ describe '#policy_handle' do
14
+ it 'is an LsaprHandle structure' do
15
+ expect(packet.policy_handle).to be_a RubySMB::Dcerpc::Lsarpc::LsaprHandle
16
+ end
17
+ end
18
+ describe '#initialize_instance' do
19
+ it 'sets #opnum to LSAR_CLOSE_HANDLE constant' do
20
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Lsarpc::LSAR_CLOSE_HANDLE)
21
+ end
22
+ end
23
+ it 'reads itself' do
24
+ new_packet = described_class.new(
25
+ policy_handle: {
26
+ context_handle_attributes: 0,
27
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
28
+ }
29
+ )
30
+ expected_output = {
31
+ policy_handle: {
32
+ context_handle_attributes: 0,
33
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
34
+ }
35
+ }
36
+ expect(packet.read(new_packet.to_binary_s)).to eq(expected_output)
37
+ end
38
+ end
39
+
40
+
@@ -0,0 +1,46 @@
1
+ RSpec.describe RubySMB::Dcerpc::Lsarpc::LsarCloseHandleResponse do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :policy_handle }
5
+ it { is_expected.to respond_to :error_status }
6
+ it { is_expected.to respond_to :opnum }
7
+
8
+ it 'is little endian' do
9
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
10
+ end
11
+ it 'is a BinData::Record' do
12
+ expect(packet).to be_a(BinData::Record)
13
+ end
14
+ describe '#policy_handle' do
15
+ it 'is a LsaprHandle structure' do
16
+ expect(packet.policy_handle).to be_a RubySMB::Dcerpc::Lsarpc::LsaprHandle
17
+ end
18
+ end
19
+ describe '#error_status' do
20
+ it 'is a NdrUint32 structure' do
21
+ expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
22
+ end
23
+ end
24
+ describe '#initialize_instance' do
25
+ it 'sets #opnum to LSAR_CLOSE_HANDLE constant' do
26
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Lsarpc::LSAR_CLOSE_HANDLE)
27
+ end
28
+ end
29
+ it 'reads itself' do
30
+ new_class = described_class.new(
31
+ policy_handle: {
32
+ context_handle_attributes: 0,
33
+ context_handle_uuid: '2ef54a87-e29e-4d24-90e9-9da49b94449e'
34
+ },
35
+ error_status: 0
36
+ )
37
+ expect(packet.read(new_class.to_binary_s)).to eq(
38
+ {
39
+ policy_handle: {
40
+ context_handle_attributes: 0,
41
+ context_handle_uuid: '2ef54a87-e29e-4d24-90e9-9da49b94449e'
42
+ },
43
+ error_status: 0
44
+ })
45
+ end
46
+ end
@@ -0,0 +1,69 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ RSpec.describe RubySMB::Dcerpc::Lsarpc::LsarLookupSidsRequest do
4
+ subject(:packet) { described_class.new }
5
+
6
+ it { is_expected.to respond_to :policy_handle }
7
+ it { is_expected.to respond_to :sid_enum_buffer }
8
+ it { is_expected.to respond_to :translated_names }
9
+ it { is_expected.to respond_to :lookup_level }
10
+ it { is_expected.to respond_to :mapped_count }
11
+ it { is_expected.to respond_to :opnum }
12
+
13
+ it 'is little endian' do
14
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
15
+ end
16
+ it 'is a BinData::Record' do
17
+ expect(packet).to be_a(BinData::Record)
18
+ end
19
+ describe '#policy_handle' do
20
+ it 'is an LsaprHandle structure' do
21
+ expect(packet.policy_handle).to be_a RubySMB::Dcerpc::Lsarpc::LsaprHandle
22
+ end
23
+ end
24
+ describe '#sid_enum_buffer' do
25
+ it 'is an LsaprSidEnumBuffer structure' do
26
+ expect(packet.sid_enum_buffer).to be_a RubySMB::Dcerpc::Lsarpc::LsaprSidEnumBuffer
27
+ end
28
+ end
29
+ describe '#translated_names' do
30
+ it 'is an LsaprTranslatedNames structure' do
31
+ expect(packet.translated_names).to be_a RubySMB::Dcerpc::Lsarpc::LsaprTranslatedNames
32
+ end
33
+ end
34
+ describe '#lookup_level' do
35
+ it 'is an NdrUint16' do
36
+ expect(packet.lookup_level).to be_a RubySMB::Dcerpc::Ndr::NdrUint16
37
+ end
38
+ end
39
+ describe '#mapped_count' do
40
+ it 'is an NdrUint32' do
41
+ expect(packet.mapped_count).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
42
+ end
43
+ end
44
+ describe '#initialize_instance' do
45
+ it 'sets #opnum to LSAR_LOOKUP_SIDS constant' do
46
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Lsarpc::LSAR_LOOKUP_SIDS)
47
+ end
48
+ end
49
+ it 'reads itself' do
50
+ new_class = described_class.new(
51
+ policy_handle: {
52
+ context_handle_attributes: 0,
53
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
54
+ },
55
+ sid_enum_buffer: { num_entries: 1, sid_info: [ { sid: 'S-1-5-21-2181772609-2124839192-2039643012-500' } ] },
56
+ lookup_level: 0,
57
+ )
58
+ expect(packet.read(new_class.to_binary_s)).to eq(
59
+ policy_handle: {
60
+ context_handle_attributes: 0,
61
+ context_handle_uuid: "fc873b90-d9a9-46a4-b9ea-f44bb1c272a7"
62
+ },
63
+ sid_enum_buffer: { num_entries: 1, sid_info: [ { sid: 'S-1-5-21-2181772609-2124839192-2039643012-500' } ] },
64
+ translated_names: { num_entries: 0, names: :null },
65
+ lookup_level: 0,
66
+ mapped_count: 0
67
+ )
68
+ end
69
+ end
@@ -0,0 +1,56 @@
1
+ require 'ruby_smb/dcerpc/ndr'
2
+
3
+ RSpec.describe RubySMB::Dcerpc::Lsarpc::LsarLookupSidsResponse do
4
+ subject(:packet) { described_class.new }
5
+
6
+ it { is_expected.to respond_to :referenced_domains }
7
+ it { is_expected.to respond_to :translated_names }
8
+ it { is_expected.to respond_to :mapped_count }
9
+ it { is_expected.to respond_to :error_status }
10
+ it { is_expected.to respond_to :opnum }
11
+
12
+ it 'is little endian' do
13
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
14
+ end
15
+ it 'is a BinData::Record' do
16
+ expect(packet).to be_a(BinData::Record)
17
+ end
18
+ describe '#referenced_domains' do
19
+ it 'is an LsaprReferencedDomainListPtr structure' do
20
+ expect(packet.referenced_domains).to be_a RubySMB::Dcerpc::Lsarpc::LsaprReferencedDomainListPtr
21
+ end
22
+ end
23
+ describe '#translated_names' do
24
+ it 'is an LsaprTranslatedNames structure' do
25
+ expect(packet.translated_names).to be_a RubySMB::Dcerpc::Lsarpc::LsaprTranslatedNames
26
+ end
27
+ end
28
+ describe '#mapped_count' do
29
+ it 'is an NdrUint32 structure' do
30
+ expect(packet.mapped_count).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
31
+ end
32
+ end
33
+ describe '#error_status' do
34
+ it 'is an NdrUint32' do
35
+ expect(packet.error_status).to be_a RubySMB::Dcerpc::Ndr::NdrUint32
36
+ end
37
+ end
38
+ describe '#initialize_instance' do
39
+ it 'sets #opnum to LSAR_LOOKUP_SIDS constant' do
40
+ expect(packet.opnum).to eq(RubySMB::Dcerpc::Lsarpc::LSAR_LOOKUP_SIDS)
41
+ end
42
+ end
43
+ it 'reads itself' do
44
+ new_class = described_class.new(
45
+ translated_names: { num_entries: 1, names: [ { use: 0, name: 'Administrator', domain_index: 0 }] },
46
+ mapped_count: 1,
47
+ error_status: 0
48
+ )
49
+ expect(packet.read(new_class.to_binary_s)).to eq(
50
+ referenced_domains: :null,
51
+ translated_names: { num_entries: 1, names: [ { use: 0, name: { buffer_length: 26, maximum_length: 26, buffer: 'Administrator'.encode('UTF-16LE') }, domain_index: 0 } ] },
52
+ mapped_count: 1,
53
+ error_status: 0
54
+ )
55
+ end
56
+ end