ruby_smb 3.3.6 → 3.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69dcf2cf8fa1b0bfe541d6c8fca903fedeb202a779a57ea8f7603122f0ffdd4a
4
- data.tar.gz: ea05a9a1c3a6c4120e56b9cd2656b70ffb7aa3f0b857596ae00104236271154c
3
+ metadata.gz: 228afeef84601354373c132ceaa48341ed9f5f4bbab4e625c37d2f2d71864146
4
+ data.tar.gz: 71512d0529ba352d0cc0ee7c27a27e03116d50f31801beed3fd04cb19e73f4ff
5
5
  SHA512:
6
- metadata.gz: 3567cb640cb9221e3bd79adfbb26b9e8a6b2f0baa7b474b61d9fb02e283c72f53148542b5a271263e1c8ea77c9e5c84935123fec5e72c6f2146c8bee563b354f
7
- data.tar.gz: 8fe76d29d6d96a63bad52c316909263e6e335819fc0bfcc04e2f5d0783c7c526ebb1b89c2c2b53798eebdcdec66954264d10b99cfdb8cccd5c4c488fba6473ad
6
+ metadata.gz: 3c7dede328c8d637b9088da518649deba6d758a1093e3591bb0cd9e2f4c458a5c5a82a37640aa14523586aa6e83b61d59d4fab21d3fa33739c47d687367cede3
7
+ data.tar.gz: 6c72f0673379264f71a55935dec05f13f195614c9cd8d6f44935687ab028545e233496ad04f4157a07d1f5f74092fac8dd43f69713d2ac1aeeb7006a12c47e21
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This example script is used for testing the Winreg registry key security descriptor functionalities.
4
+ # It will attempt to connect to a host and reads (or writes) the security descriptor of a specified registry key.
5
+ #
6
+ # Example usage:
7
+ # - read:
8
+ # ruby examples/read_registry_key_security.rb --username msfadmin --password msfadmin -i 7 -o r 192.168.172.138 'HKLM\SECURITY\Policy\PolEKList'
9
+ # This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin
10
+ # credentialas and read the security descriptor of the
11
+ # `HKLM\SECURITY\Policy\PolEKList` registry key with the security information 7
12
+ # (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
13
+ # DACL_SECURITY_INFORMATION).
14
+ #
15
+ # - write:
16
+ # ruby examples/read_registry_key_security.rb --username msfadmin --password msfadmin -i 4 --sd 01000480000000000000000000000000140000000200340002000000000214003f000f00010100000000000512000000000218000000060001020000000000052000000020020000 -o w 192.168.172.138 'HKLM\SECURITY\Policy\PolEKList'
17
+ # This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin
18
+ # credentialas and write the given security descriptor to the
19
+ # `HKLM\SECURITY\Policy\PolEKList` registry key with the security information 4
20
+ # (DACL_SECURITY_INFORMATION).
21
+
22
+ require 'bundler/setup'
23
+ require 'optparse'
24
+ require 'ruby_smb'
25
+
26
+ OPERATIONS = %w{read write}
27
+ OPERATION_ALIASES = { "r" => "read", "w" => "write" }
28
+
29
+ args = ARGV.dup
30
+ options = {
31
+ domain: '.',
32
+ username: '',
33
+ password: '',
34
+ smbv1: true,
35
+ smbv2: true,
36
+ smbv3: true,
37
+ target: nil,
38
+ key: nil,
39
+ operation: 'read',
40
+ info: RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION | RubySMB::Field::SecurityDescriptor::GROUP_SECURITY_INFORMATION | RubySMB::Field::SecurityDescriptor::DACL_SECURITY_INFORMATION,
41
+ sd: nil
42
+ }
43
+ options[:key] = args.pop
44
+ options[:target ] = args.pop
45
+ optparser = OptionParser.new do |opts|
46
+ opts.banner = "Usage: #{File.basename(__FILE__)} [options] target reg_key"
47
+ opts.on('--[no-]smbv1', "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
48
+ options[:smbv1] = smbv1
49
+ end
50
+ opts.on('--[no-]smbv2', "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
51
+ options[:smbv2] = smbv2
52
+ end
53
+ opts.on('--[no-]smbv3', "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
54
+ options[:smbv3] = smbv3
55
+ end
56
+ opts.on('-u', '--username [USERNAME]', "The account's username (default: #{options[:username]})") do |username|
57
+ if username.include?('\\')
58
+ options[:domain], options[:username] = username.split('\\', 2)
59
+ else
60
+ options[:username] = username
61
+ end
62
+ end
63
+ opts.on('-p', '--password [PASSWORD]', "The account's password (default: #{options[:password]})") do |password|
64
+ options[:password] = password
65
+ end
66
+ operation_list = (OPERATION_ALIASES.keys + OPERATIONS).join(', ')
67
+ opts.on('-o', '--operation OPERATION', OPERATIONS, OPERATION_ALIASES, "The operation to perform on the registry key (default: #{options[:operation]})", "(#{operation_list})") do |operation|
68
+ options[:operation] = operation
69
+ end
70
+ opts.on('-i', '--info [SECURITY INFORMATION]', Integer, "The security information value (default: #{options[:info]})") do |password|
71
+ options[:info] = password
72
+ end
73
+ opts.on('-s', '--sd [SECURITY DESCRIPTOR]', "The security descriptor to write as an hex string") do |sd|
74
+ options[:sd] = sd
75
+ end
76
+ end
77
+ optparser.parse!(args)
78
+
79
+ if options[:target].nil? || options[:key].nil?
80
+ abort(optparser.help)
81
+ end
82
+
83
+ sock = TCPSocket.new options[:target], 445
84
+ dispatcher = RubySMB::Dispatcher::Socket.new(sock)
85
+
86
+ client = RubySMB::Client.new(dispatcher, smb1: options[:smbv1], smb2: options[:smbv2], smb3: options[:smbv3], username: options[:username], password: options[:password], domain: options[:domain])
87
+ protocol = client.negotiate
88
+ status = client.authenticate
89
+
90
+ puts "#{protocol}: #{status}"
91
+
92
+ case options[:operation]
93
+ when 'read', 'r'
94
+ puts "Read registry key #{options[:key]} security descriptor with security information #{options[:info]}"
95
+ security_descriptor = client.get_key_security_descriptor(options[:target], options[:key], options[:info])
96
+ puts "Security descriptor: #{security_descriptor.b.bytes.map {|c| "%02x" % c.ord}.join}"
97
+ when 'write', 'w'
98
+ unless options[:sd] && !options[:sd].empty?
99
+ puts "Security descriptor missing"
100
+ abort(optparser.help)
101
+ end
102
+ puts "Write security descriptor #{options[:sd]} to registry key #{options[:key]} with security information #{options[:info]}"
103
+ sd = options[:sd].chars.each_slice(2).map {|c| c.join.to_i(16).chr}.join
104
+ status = client.set_key_security_descriptor(options[:target], options[:key], sd, options[:info])
105
+ puts "Success!"
106
+ end
107
+
108
+ client.disconnect!
109
+
@@ -40,6 +40,18 @@ module RubySMB
40
40
  end
41
41
  end
42
42
 
43
+ def get_key_security_descriptor(host, key, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION)
44
+ connect_to_winreg(host) do |named_pipe|
45
+ named_pipe.get_key_security_descriptor(key, security_information)
46
+ end
47
+ end
48
+
49
+ def set_key_security_descriptor(host, key, security_descriptor, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION)
50
+ connect_to_winreg(host) do |named_pipe|
51
+ named_pipe.set_key_security_descriptor(key, security_descriptor, security_information)
52
+ end
53
+ end
54
+
43
55
  end
44
56
  end
45
57
  end
@@ -567,8 +567,11 @@ module RubySMB::Dcerpc::Ndr
567
567
  def get_max_count(val)
568
568
  if is_a?(BinData::Stringz)
569
569
  max_count = val.to_s.strip.length
570
- # Only count the terminating NULL byte if the string is not empty
571
- max_count += 1 if max_count > 0
570
+ # Add one to count the terminator. According to
571
+ # https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02,
572
+ # the NDR String must contain at least one element, the terminator. So,
573
+ # add one even if it is an empty string.
574
+ max_count += 1
572
575
  return max_count
573
576
  else
574
577
  return val.to_s.length
@@ -622,8 +625,11 @@ module RubySMB::Dcerpc::Ndr
622
625
  def update_actual_count(val)
623
626
  if is_a?(BinData::Stringz)
624
627
  @actual_count = val.to_s.strip.length
625
- # Only count the terminating NULL byte if the string is not empty
626
- @actual_count += 1 if @actual_count > 0
628
+ # Add one to count the terminator. According to
629
+ # https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04,
630
+ # the NDR String must contain at least one element, the terminator. So,
631
+ # add one even if it is an empty string.
632
+ @actual_count += 1
627
633
  else
628
634
  @actual_count = val.to_s.length
629
635
  end
@@ -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
@@ -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
@@ -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.7'.freeze
3
3
  end
@@ -1352,6 +1352,15 @@ RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarString do
1352
1352
  }
1353
1353
  let(:value) { 'ABCD' }
1354
1354
  end
1355
+ context 'with an empty string' do
1356
+ it_behaves_like 'a NDR String', conformant: false, char_size: 1, null_terminated: false do
1357
+ let(:binary_stream) {
1358
+ "\x00\x00\x00\x00"\
1359
+ "\x00\x00\x00\x00".b
1360
+ }
1361
+ let(:value) { '' }
1362
+ end
1363
+ end
1355
1364
  end
1356
1365
 
1357
1366
  RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarStringz do
@@ -1368,6 +1377,16 @@ RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarStringz do
1368
1377
  }
1369
1378
  let(:value) { 'ABCD' }
1370
1379
  end
1380
+ context 'with an empty string' do
1381
+ it_behaves_like 'a NDR String', conformant: false, char_size: 1, null_terminated: true do
1382
+ let(:binary_stream) {
1383
+ "\x00\x00\x00\x00"\
1384
+ "\x01\x00\x00\x00"\
1385
+ "\x00".b
1386
+ }
1387
+ let(:value) { '' }
1388
+ end
1389
+ end
1371
1390
  end
1372
1391
 
1373
1392
  RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarWideString do
@@ -1383,6 +1402,15 @@ RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarWideString do
1383
1402
  }
1384
1403
  let(:value) { 'ABCD'.encode('utf-16le') }
1385
1404
  end
1405
+ context 'with an empty string' do
1406
+ it_behaves_like 'a NDR String', conformant: false, char_size: 2, null_terminated: false do
1407
+ let(:binary_stream) {
1408
+ "\x00\x00\x00\x00"\
1409
+ "\x00\x00\x00\x00".b
1410
+ }
1411
+ let(:value) { '' }
1412
+ end
1413
+ end
1386
1414
  end
1387
1415
 
1388
1416
  RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarWideStringz do
@@ -1398,6 +1426,16 @@ RSpec.describe RubySMB::Dcerpc::Ndr::NdrVarWideStringz do
1398
1426
  }
1399
1427
  let(:value) { 'ABCD'.encode('utf-16le') }
1400
1428
  end
1429
+ context 'with an empty string' do
1430
+ it_behaves_like 'a NDR String', conformant: false, char_size: 2, null_terminated: true do
1431
+ let(:binary_stream) {
1432
+ "\x00\x00\x00\x00"\
1433
+ "\x01\x00\x00\x00"\
1434
+ "\x00\x00".b
1435
+ }
1436
+ let(:value) { '' }
1437
+ end
1438
+ end
1401
1439
  end
1402
1440
 
1403
1441
  RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarString do
@@ -1415,6 +1453,16 @@ RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarString do
1415
1453
  }
1416
1454
  let(:value) { 'ABCD' }
1417
1455
  end
1456
+ context 'with an empty string' do
1457
+ it_behaves_like 'a NDR String', conformant: true, char_size: 1, null_terminated: false do
1458
+ let(:binary_stream) {
1459
+ "\x00\x00\x00\x00"\
1460
+ "\x00\x00\x00\x00"\
1461
+ "\x00\x00\x00\x00".b
1462
+ }
1463
+ let(:value) { '' }
1464
+ end
1465
+ end
1418
1466
  end
1419
1467
 
1420
1468
  RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarStringz do
@@ -1432,6 +1480,17 @@ RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarStringz do
1432
1480
  }
1433
1481
  let(:value) { 'ABCD' }
1434
1482
  end
1483
+ context 'with an empty string' do
1484
+ it_behaves_like 'a NDR String', conformant: true, char_size: 1, null_terminated: true do
1485
+ let(:binary_stream) {
1486
+ "\x01\x00\x00\x00"\
1487
+ "\x00\x00\x00\x00"\
1488
+ "\x01\x00\x00\x00"\
1489
+ "\x00".b
1490
+ }
1491
+ let(:value) { '' }
1492
+ end
1493
+ end
1435
1494
  end
1436
1495
 
1437
1496
  RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarWideString do
@@ -1448,6 +1507,16 @@ RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarWideString do
1448
1507
  }
1449
1508
  let(:value) { 'ABCD'.encode('utf-16le') }
1450
1509
  end
1510
+ context 'with an empty string' do
1511
+ it_behaves_like 'a NDR String', conformant: true, char_size: 2, null_terminated: false do
1512
+ let(:binary_stream) {
1513
+ "\x00\x00\x00\x00"\
1514
+ "\x00\x00\x00\x00"\
1515
+ "\x00\x00\x00\x00".b
1516
+ }
1517
+ let(:value) { '' }
1518
+ end
1519
+ end
1451
1520
  end
1452
1521
 
1453
1522
  RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarWideStringz do
@@ -1464,6 +1533,17 @@ RSpec.describe RubySMB::Dcerpc::Ndr::NdrConfVarWideStringz do
1464
1533
  }
1465
1534
  let(:value) { 'ABCD'.encode('utf-16le') }
1466
1535
  end
1536
+ context 'with an empty string' do
1537
+ it_behaves_like 'a NDR String', conformant: true, char_size: 1, null_terminated: true do
1538
+ let(:binary_stream) {
1539
+ "\x01\x00\x00\x00"\
1540
+ "\x00\x00\x00\x00"\
1541
+ "\x01\x00\x00\x00"\
1542
+ "\x00\x00".b
1543
+ }
1544
+ let(:value) { '' }
1545
+ end
1546
+ end
1467
1547
  end
1468
1548
 
1469
1549
 
@@ -73,12 +73,7 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
73
73
  before :example do
74
74
  allow(described_class::OpenKeyRequest).to receive(:new).and_return(openkey_request_packet)
75
75
  allow(openkey_request_packet).to receive(:sam_desired).and_return(regsam)
76
- allow(regsam).to receive_messages(
77
- :read_control= => nil,
78
- :key_query_value= => nil,
79
- :key_enumerate_sub_keys= => nil,
80
- :key_notify= => nil,
81
- )
76
+ allow(regsam).to receive(:maximum_allowed=)
82
77
  allow(winreg).to receive(:dcerpc_request).and_return(response)
83
78
  allow(described_class::OpenKeyResponse).to receive(:read).and_return(open_key_response)
84
79
  allow(open_key_response).to receive_messages(
@@ -94,10 +89,7 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
94
89
 
95
90
  it 'sets the expected user rights on the request packet' do
96
91
  winreg.open_key(handle, sub_key)
97
- expect(regsam).to have_received(:read_control=).with(1)
98
- expect(regsam).to have_received(:key_query_value=).with(1)
99
- expect(regsam).to have_received(:key_enumerate_sub_keys=).with(1)
100
- expect(regsam).to have_received(:key_notify=).with(1)
92
+ expect(regsam).to have_received(:maximum_allowed=).with(1)
101
93
  end
102
94
 
103
95
  it 'sends the expected dcerpc request' do
@@ -132,12 +124,13 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
132
124
  describe '#query_value' do
133
125
  let(:handle) { double('Handle') }
134
126
  let(:value_name) { double('Value Name') }
135
- let(:query_value_request_packet) { double('Query Value Request Packet #1') }
136
- let(:lp_data) { double('LpData #2') }
127
+ let(:query_value_request_packet) { double('Query Value Request Packet #1') }
128
+ let(:lp_data) { double('LpData #2') }
137
129
  let(:response1) { double('Response #1') }
138
130
  let(:response2) { double('Response #2') }
139
131
  let(:query_value_response1) { double('Query Value Response #1') }
140
132
  let(:query_value_response2) { double('Query Value Response #2') }
133
+ let(:lp_type) { double('Type') }
141
134
  let(:data) { double('Data') }
142
135
  let(:lpcb_data) { double('LpcbData') }
143
136
  let(:max_count) { 5 }
@@ -164,8 +157,9 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
164
157
  allow(described_class::QueryValueResponse).to receive(:read).with(response2).and_return(query_value_response2)
165
158
  allow(query_value_response1).to receive(:error_status).and_return(WindowsError::Win32::ERROR_SUCCESS)
166
159
  allow(query_value_response2).to receive_messages(
167
- :error_status => WindowsError::Win32::ERROR_SUCCESS,
168
- :data => data
160
+ error_status: WindowsError::Win32::ERROR_SUCCESS,
161
+ lp_type: lp_type,
162
+ data: data
169
163
  )
170
164
  allow(query_value_response1).to receive(:lpcb_data).and_return(lpcb_data)
171
165
  allow(lpcb_data).to receive(:to_i).and_return(max_count)
@@ -234,8 +228,9 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
234
228
  end
235
229
 
236
230
  it 'returns the expected response data' do
237
- expect(winreg.query_value(handle, value_name)).to eq(data)
231
+ expect(winreg.query_value(handle, value_name)).to eq(RubySMB::Dcerpc::Winreg::RegValue.new(lp_type, data))
238
232
  end
233
+
239
234
  end
240
235
 
241
236
  describe '#close_key' do
@@ -529,13 +524,14 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
529
524
  let(:value_name) { 'registry_value_name' }
530
525
  let(:root_key_handle) { double('Root Key Handle') }
531
526
  let(:subkey_handle) { double('Subkey Handle') }
532
- let(:value) { double('Value') }
527
+ let(:data) { double('Reg value data') }
528
+ let(:reg_value) { RubySMB::Dcerpc::Winreg::RegValue.new(nil, data) }
533
529
  before :example do
534
530
  allow(winreg).to receive_messages(
535
531
  :bind => nil,
536
532
  :open_root_key => root_key_handle,
537
533
  :open_key => subkey_handle,
538
- :query_value => value,
534
+ :query_value => reg_value,
539
535
  :close_key => nil
540
536
  )
541
537
  end
@@ -572,7 +568,7 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
572
568
  end
573
569
 
574
570
  it 'returns expect registry key value' do
575
- expect(winreg.read_registry_key_value(key, value_name)).to eq(value)
571
+ expect(winreg.read_registry_key_value(key, value_name)).to eq(data)
576
572
  end
577
573
  end
578
574
 
@@ -864,4 +860,257 @@ RSpec.describe RubySMB::Dcerpc::Winreg do
864
860
  end
865
861
  end
866
862
  end
863
+
864
+ describe '#get_key_security_descriptor' do
865
+ let(:root_key) { 'HKLM' }
866
+ let(:sub_key) { 'my\\sub\\key\\path' }
867
+ let(:key) { "#{root_key}\\#{sub_key}" }
868
+ let(:root_key_handle) { double('Root Key Handle') }
869
+ let(:subkey_handle) { double('Subkey Handle') }
870
+ before :example do
871
+ allow(winreg).to receive_messages(
872
+ :bind => nil,
873
+ :open_root_key => root_key_handle,
874
+ :open_key => subkey_handle,
875
+ :get_key_security => nil,
876
+ :close_key => nil
877
+ )
878
+ end
879
+
880
+ it 'binds a DCERPC connection to the expected remote endpoint' do
881
+ winreg.get_key_security_descriptor(key)
882
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
883
+ end
884
+
885
+ it 'does not bind a DCERPC connection if #bind argument is false' do
886
+ winreg.get_key_security_descriptor(key, bind: false)
887
+ expect(winreg).to_not have_received(:bind)
888
+ end
889
+
890
+ it 'opens the expected root key' do
891
+ winreg.get_key_security_descriptor(key)
892
+ expect(winreg).to have_received(:open_root_key).with(root_key)
893
+ end
894
+
895
+ it 'opens the expected registry key' do
896
+ winreg.get_key_security_descriptor(key)
897
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
898
+ end
899
+
900
+ it 'calls #get_key_security with the expected arguments' do
901
+ winreg.get_key_security_descriptor(key)
902
+ security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION
903
+ expect(winreg).to have_received(:get_key_security).with(subkey_handle, security_information)
904
+ end
905
+
906
+ context 'with a non-default security informaiton' do
907
+ it 'calls #get_key_security with the expected arguments' do
908
+ security_information = RubySMB::Field::SecurityDescriptor::GROUP_SECURITY_INFORMATION
909
+ winreg.get_key_security_descriptor(key, security_information)
910
+ expect(winreg).to have_received(:get_key_security).with(subkey_handle, security_information)
911
+ end
912
+ end
913
+ end
914
+
915
+ describe '#get_key_security' do
916
+ let(:handle) { double('Handle') }
917
+ let(:get_key_security_request) { double('GetKeySecurity Request') }
918
+ let(:response) {
919
+ '0000020000100000940000000010000000000000940000000100048078000000880000'\
920
+ '0000000000140000000200640004000000000214003f000f0001010000000000051200'\
921
+ '0000000218000000060001020000000000052000000020020000000218000900060001'\
922
+ '0200000000000520000000200200000002180009000600010200000000000520000000'\
923
+ '2002000001020000000000052000000020020000010100000000000512000000000000'\
924
+ '00'.unhexlify
925
+ }
926
+ let(:security_descriptor) {
927
+ '01000480780000008800000000000000140000000200640004000000000214003f000f'\
928
+ '0001010000000000051200000000021800000006000102000000000005200000002002'\
929
+ '0000000218000900060001020000000000052000000020020000000218000900060001'\
930
+ '0200000000000520000000200200000102000000000005200000002002000001010000'\
931
+ '0000000512000000'.unhexlify
932
+ }
933
+ before :example do
934
+ allow(described_class::GetKeySecurityRequest).to receive(:new).and_return(get_key_security_request)
935
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
936
+ end
937
+
938
+ it 'create the expected GetKeySecurityRequest packet with the default options' do
939
+ opts = {
940
+ hkey: handle,
941
+ security_information: RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION,
942
+ prpc_security_descriptor_in: { cb_in_security_descriptor: 4096 }
943
+ }
944
+ winreg.get_key_security(handle)
945
+ expect(described_class::GetKeySecurityRequest).to have_received(:new).with(opts)
946
+ end
947
+
948
+ it 'create the expected SaveKeyRequest packet with custom options' do
949
+ security_information = RubySMB::Field::SecurityDescriptor::GROUP_SECURITY_INFORMATION
950
+ opts = {
951
+ hkey: handle,
952
+ security_information: security_information,
953
+ prpc_security_descriptor_in: { cb_in_security_descriptor: 4096 }
954
+ }
955
+ winreg.get_key_security(handle, security_information)
956
+ expect(described_class::GetKeySecurityRequest).to have_received(:new).with(opts)
957
+ end
958
+
959
+ it 'sends the expected dcerpc request' do
960
+ winreg.get_key_security(handle)
961
+ expect(winreg).to have_received(:dcerpc_request).with(get_key_security_request)
962
+ end
963
+
964
+ it 'creates a GetKeySecurityResponse structure from the expected dcerpc response' do
965
+ expect(described_class::GetKeySecurityResponse).to receive(:read).with(response).and_call_original
966
+ winreg.get_key_security(handle)
967
+ end
968
+
969
+ context 'when an IOError occurs while parsing the response' do
970
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
971
+ allow(described_class::GetKeySecurityResponse).to receive(:read).and_raise(IOError)
972
+ expect { winreg.get_key_security(handle) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
973
+ end
974
+ end
975
+
976
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
977
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
978
+ response[-4..-1] = [WindowsError::Win32::ERROR_INVALID_DATA.value].pack('V')
979
+ expect { winreg.get_key_security(handle) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
980
+ end
981
+ end
982
+
983
+ it 'returns the expected security descriptor' do
984
+ expect(winreg.get_key_security(handle)).to eq(security_descriptor)
985
+ end
986
+ end
987
+
988
+ describe '#set_key_security_descriptor' do
989
+ let(:root_key) { 'HKLM' }
990
+ let(:sub_key) { 'my\\sub\\key\\path' }
991
+ let(:key) { "#{root_key}\\#{sub_key}" }
992
+ let(:security_descriptor) { 'Security Descriptor' }
993
+ let(:root_key_handle) { double('Root Key Handle') }
994
+ let(:subkey_handle) { double('Subkey Handle') }
995
+ before :example do
996
+ allow(winreg).to receive_messages(
997
+ :bind => nil,
998
+ :open_root_key => root_key_handle,
999
+ :open_key => subkey_handle,
1000
+ :set_key_security => nil,
1001
+ :close_key => nil
1002
+ )
1003
+ end
1004
+
1005
+ it 'binds a DCERPC connection to the expected remote endpoint' do
1006
+ winreg.set_key_security_descriptor(key, security_descriptor)
1007
+ expect(winreg).to have_received(:bind).with(endpoint: RubySMB::Dcerpc::Winreg)
1008
+ end
1009
+
1010
+ it 'does not bind a DCERPC connection if #bind argument is false' do
1011
+ winreg.set_key_security_descriptor(key, security_descriptor, bind: false)
1012
+ expect(winreg).to_not have_received(:bind)
1013
+ end
1014
+
1015
+ it 'opens the expected root key' do
1016
+ winreg.set_key_security_descriptor(key, security_descriptor)
1017
+ expect(winreg).to have_received(:open_root_key).with(root_key)
1018
+ end
1019
+
1020
+ it 'opens the expected registry key' do
1021
+ winreg.set_key_security_descriptor(key, security_descriptor)
1022
+ expect(winreg).to have_received(:open_key).with(root_key_handle, sub_key)
1023
+ end
1024
+
1025
+ it 'calls #set_key_security with the expected arguments' do
1026
+ winreg.set_key_security_descriptor(key, security_descriptor)
1027
+ security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION
1028
+ expect(winreg).to have_received(:set_key_security).with(subkey_handle, security_descriptor, security_information)
1029
+ end
1030
+
1031
+ context 'with a non-default security informaiton' do
1032
+ it 'calls #get_key_security with the expected arguments' do
1033
+ security_information = RubySMB::Field::SecurityDescriptor::GROUP_SECURITY_INFORMATION
1034
+ winreg.set_key_security_descriptor(key, security_descriptor, security_information)
1035
+ expect(winreg).to have_received(:set_key_security).with(subkey_handle, security_descriptor, security_information)
1036
+ end
1037
+ end
1038
+ end
1039
+
1040
+ describe '#set_key_security' do
1041
+ let(:handle) { double('Handle') }
1042
+ let(:set_key_security_request) { double('GetKeySecurity Request') }
1043
+ let(:response) { '00000000'.unhexlify }
1044
+ let(:security_descriptor) {
1045
+ '0100048014000000240000000000000030000000010200000000000520000000200200'\
1046
+ '0001010000000000051200000002007c0005000000000214003f000f00010100000000'\
1047
+ '0005120000000002180000000600010200000000000520000000200200000002180009'\
1048
+ '0006000102000000000005200000002002000000021800090006000102000000000005'\
1049
+ '2000000020020000000218000900060001020000000000052000000020020000'.unhexlify
1050
+ }
1051
+ before :example do
1052
+ allow(described_class::SetKeySecurityRequest).to receive(:new).and_return(set_key_security_request)
1053
+ allow(winreg).to receive(:dcerpc_request).and_return(response)
1054
+ end
1055
+
1056
+ it 'create the expected SetKeySecurityRequest packet with the default options' do
1057
+ opts = {
1058
+ hkey: handle,
1059
+ security_information: RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION,
1060
+ prpc_security_descriptor: {
1061
+ lp_security_descriptor: security_descriptor.bytes,
1062
+ cb_in_security_descriptor: security_descriptor.size,
1063
+ cb_out_security_descriptor: security_descriptor.size
1064
+ }
1065
+ }
1066
+ winreg.set_key_security(handle, security_descriptor)
1067
+ expect(described_class::SetKeySecurityRequest).to have_received(:new).with(opts)
1068
+ end
1069
+
1070
+ it 'create the expected SaveKeyRequest packet with custom options' do
1071
+ security_information = RubySMB::Field::SecurityDescriptor::GROUP_SECURITY_INFORMATION
1072
+ opts = {
1073
+ hkey: handle,
1074
+ security_information: security_information,
1075
+ prpc_security_descriptor: {
1076
+ lp_security_descriptor: security_descriptor.bytes,
1077
+ cb_in_security_descriptor: security_descriptor.size,
1078
+ cb_out_security_descriptor: security_descriptor.size
1079
+ }
1080
+ }
1081
+ winreg.set_key_security(handle, security_descriptor, security_information)
1082
+ expect(described_class::SetKeySecurityRequest).to have_received(:new).with(opts)
1083
+ end
1084
+
1085
+ it 'sends the expected dcerpc request' do
1086
+ winreg.set_key_security(handle, security_descriptor)
1087
+ expect(winreg).to have_received(:dcerpc_request).with(set_key_security_request)
1088
+ end
1089
+
1090
+ it 'creates a SetKeySecurityResponse structure from the expected dcerpc response' do
1091
+ expect(described_class::SetKeySecurityResponse).to receive(:read).with(response).and_call_original
1092
+ winreg.set_key_security(handle, security_descriptor)
1093
+ end
1094
+
1095
+ context 'when an IOError occurs while parsing the response' do
1096
+ it 'raises a RubySMB::Dcerpc::Error::InvalidPacket' do
1097
+ allow(described_class::SetKeySecurityResponse).to receive(:read).and_raise(IOError)
1098
+ expect { winreg.set_key_security(handle, security_descriptor) }.to raise_error(RubySMB::Dcerpc::Error::InvalidPacket)
1099
+ end
1100
+ end
1101
+
1102
+ context 'when the response error status is not WindowsError::Win32::ERROR_SUCCESS' do
1103
+ it 'raises a RubySMB::Dcerpc::Error::WinregError' do
1104
+ response[-4..-1] = [WindowsError::Win32::ERROR_INVALID_DATA.value].pack('V')
1105
+ expect { winreg.set_key_security(handle, security_descriptor) }.to raise_error(RubySMB::Dcerpc::Error::WinregError)
1106
+ end
1107
+ end
1108
+
1109
+ it 'returns the expected error status' do
1110
+ expect(winreg.set_key_security(handle, security_descriptor)).to eq(WindowsError::Win32::ERROR_SUCCESS)
1111
+ end
1112
+ end
1113
+
1114
+
1115
+
867
1116
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_smb
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.6
4
+ version: 3.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Metasploit Hackers
@@ -38,7 +38,7 @@ cert_chain:
38
38
  DgscAao7wB3xW2BWEp1KnaDWkf1x9ttgoBEYyuYwU7uatB67kBQG1PKvLt79wHvz
39
39
  Dxs+KOjGbBRfMnPgVGYkORKVrZIwlaboHbDKxcVW5xv+oZc7KYXWGg==
40
40
  -----END CERTIFICATE-----
41
- date: 2024-04-25 00:00:00.000000000 Z
41
+ date: 2024-04-30 00:00:00.000000000 Z
42
42
  dependencies:
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: redcarpet
@@ -219,6 +219,7 @@ files:
219
219
  - examples/read_file.rb
220
220
  - examples/read_file_encryption.rb
221
221
  - examples/read_registry_key_value.rb
222
+ - examples/registry_key_security_descriptor.rb
222
223
  - examples/rename_file.rb
223
224
  - examples/tree_connect.rb
224
225
  - examples/virtual_file_server.rb
@@ -375,6 +376,8 @@ files:
375
376
  - lib/ruby_smb/dcerpc/winreg/enum_key_response.rb
376
377
  - lib/ruby_smb/dcerpc/winreg/enum_value_request.rb
377
378
  - lib/ruby_smb/dcerpc/winreg/enum_value_response.rb
379
+ - lib/ruby_smb/dcerpc/winreg/get_key_security_request.rb
380
+ - lib/ruby_smb/dcerpc/winreg/get_key_security_response.rb
378
381
  - lib/ruby_smb/dcerpc/winreg/open_key_request.rb
379
382
  - lib/ruby_smb/dcerpc/winreg/open_key_response.rb
380
383
  - lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb
@@ -386,6 +389,8 @@ files:
386
389
  - lib/ruby_smb/dcerpc/winreg/regsam.rb
387
390
  - lib/ruby_smb/dcerpc/winreg/save_key_request.rb
388
391
  - lib/ruby_smb/dcerpc/winreg/save_key_response.rb
392
+ - lib/ruby_smb/dcerpc/winreg/set_key_security_request.rb
393
+ - lib/ruby_smb/dcerpc/winreg/set_key_security_response.rb
389
394
  - lib/ruby_smb/dcerpc/wkssvc.rb
390
395
  - lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_request.rb
391
396
  - lib/ruby_smb/dcerpc/wkssvc/netr_wksta_get_info_response.rb
@@ -959,7 +964,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
959
964
  - !ruby/object:Gem::Version
960
965
  version: '0'
961
966
  requirements: []
962
- rubygems_version: 3.4.18
967
+ rubygems_version: 3.1.4
963
968
  signing_key:
964
969
  specification_version: 4
965
970
  summary: A pure Ruby implementation of the SMB Protocol Family
metadata.gz.sig CHANGED
Binary file