ruby_smb 3.3.6 → 3.3.8

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.
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