ruby_smb 3.3.5 → 3.3.7

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