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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/examples/registry_key_security_descriptor.rb +109 -0
- data/lib/ruby_smb/client/winreg.rb +12 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +10 -4
- data/lib/ruby_smb/dcerpc/request.rb +18 -16
- data/lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb +1 -1
- data/lib/ruby_smb/dcerpc/winreg/get_key_security_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/winreg/get_key_security_response.rb +26 -0
- data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +2 -0
- data/lib/ruby_smb/dcerpc/winreg/set_key_security_request.rb +26 -0
- data/lib/ruby_smb/dcerpc/winreg/set_key_security_response.rb +25 -0
- data/lib/ruby_smb/dcerpc/winreg.rb +121 -9
- data/lib/ruby_smb/field/security_descriptor.rb +17 -0
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +80 -0
- data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +267 -18
- data.tar.gz.sig +0 -0
- metadata +8 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 228afeef84601354373c132ceaa48341ed9f5f4bbab4e625c37d2f2d71864146
|
4
|
+
data.tar.gz: 71512d0529ba352d0cc0ee7c27a27e03116d50f31801beed3fd04cb19e73f4ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/ruby_smb/dcerpc/ndr.rb
CHANGED
@@ -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
|
-
#
|
571
|
-
|
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
|
-
#
|
626
|
-
|
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
|
22
|
-
open_root_key_request
|
23
|
-
open_root_key_request
|
24
|
-
open_root_key_request
|
25
|
-
open_root_key_request
|
26
|
-
open_root_key_request
|
27
|
-
open_root_key_request
|
28
|
-
open_root_key_request
|
29
|
-
close_key_request
|
30
|
-
enum_key_request
|
31
|
-
enum_value_request
|
32
|
-
open_key_request
|
33
|
-
query_info_key_request
|
34
|
-
query_value_request
|
35
|
-
create_key_request
|
36
|
-
save_key_request
|
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
|
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
|
+
|
@@ -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.
|
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
|
-
# @
|
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
|
-
|
356
|
-
|
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'
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -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
|
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(:
|
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)
|
136
|
-
let(:lp_data)
|
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
|
-
:
|
168
|
-
:
|
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(:
|
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 =>
|
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(
|
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.
|
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-
|
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
|
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
|