ruby_smb 3.3.6 → 3.3.7

Sign up to get free protection for your applications and to get access to all the features.
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