ruby_smb 3.3.5 → 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.
@@ -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.5'.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