Skip to content

Commit

Permalink
WIP: Add GetKeySecurity and SetKeySecurity MS-RRP structures (DCERPC)
Browse files Browse the repository at this point in the history
  • Loading branch information
cdelafuente-r7 committed Apr 2, 2024
1 parent 3e954a2 commit f10e7e8
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 27 deletions.
12 changes: 12 additions & 0 deletions lib/ruby_smb/client/winreg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ def enum_registry_values(host, key)
end
end

def get_security_descriptor(host, key, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION)
connect_to_winreg(host) do |named_pipe|
named_pipe.get_security_descriptor(key, security_information)
end
end

def set_security_descriptor(host, key, security_descriptor, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION)
connect_to_winreg(host) do |named_pipe|
named_pipe.set_security_descriptor(key, security_descriptor, security_information)
end
end

end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/ruby_smb/dcerpc/ndr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ def get_max_count(val)
if is_a?(BinData::Stringz)
max_count = val.to_s.strip.length
# Only count the terminating NULL byte if the string is not empty
max_count += 1 if max_count > 0
max_count += 1# if max_count > 0
return max_count
else
return val.to_s.length
Expand Down Expand Up @@ -619,7 +619,7 @@ def update_actual_count(val)
if is_a?(BinData::Stringz)
@actual_count = val.to_s.strip.length
# Only count the terminating NULL byte if the string is not empty
@actual_count += 1 if @actual_count > 0
@actual_count += 1# if @actual_count > 0
else
@actual_count = val.to_s.length
end
Expand Down
34 changes: 18 additions & 16 deletions lib/ruby_smb/dcerpc/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,24 @@ class Request < BinData::Record
choice :stub, label: 'Stub', selection: -> { @obj.parent.get_parameter(:endpoint) || '' } do
string 'Encrypted'
choice 'Winreg', selection: -> { opnum } do
open_root_key_request Winreg::OPEN_HKCR, opnum: Winreg::OPEN_HKCR
open_root_key_request Winreg::OPEN_HKCU, opnum: Winreg::OPEN_HKCU
open_root_key_request Winreg::OPEN_HKLM, opnum: Winreg::OPEN_HKLM
open_root_key_request Winreg::OPEN_HKPD, opnum: Winreg::OPEN_HKPD
open_root_key_request Winreg::OPEN_HKU, opnum: Winreg::OPEN_HKU
open_root_key_request Winreg::OPEN_HKCC, opnum: Winreg::OPEN_HKCC
open_root_key_request Winreg::OPEN_HKPT, opnum: Winreg::OPEN_HKPT
open_root_key_request Winreg::OPEN_HKPN, opnum: Winreg::OPEN_HKPN
close_key_request Winreg::REG_CLOSE_KEY
enum_key_request Winreg::REG_ENUM_KEY
enum_value_request Winreg::REG_ENUM_VALUE
open_key_request Winreg::REG_OPEN_KEY
query_info_key_request Winreg::REG_QUERY_INFO_KEY
query_value_request Winreg::REG_QUERY_VALUE
create_key_request Winreg::REG_CREATE_KEY
save_key_request Winreg::REG_SAVE_KEY
open_root_key_request Winreg::OPEN_HKCR, opnum: Winreg::OPEN_HKCR
open_root_key_request Winreg::OPEN_HKCU, opnum: Winreg::OPEN_HKCU
open_root_key_request Winreg::OPEN_HKLM, opnum: Winreg::OPEN_HKLM
open_root_key_request Winreg::OPEN_HKPD, opnum: Winreg::OPEN_HKPD
open_root_key_request Winreg::OPEN_HKU, opnum: Winreg::OPEN_HKU
open_root_key_request Winreg::OPEN_HKCC, opnum: Winreg::OPEN_HKCC
open_root_key_request Winreg::OPEN_HKPT, opnum: Winreg::OPEN_HKPT
open_root_key_request Winreg::OPEN_HKPN, opnum: Winreg::OPEN_HKPN
close_key_request Winreg::REG_CLOSE_KEY
enum_key_request Winreg::REG_ENUM_KEY
enum_value_request Winreg::REG_ENUM_VALUE
open_key_request Winreg::REG_OPEN_KEY
query_info_key_request Winreg::REG_QUERY_INFO_KEY
query_value_request Winreg::REG_QUERY_VALUE
create_key_request Winreg::REG_CREATE_KEY
save_key_request Winreg::REG_SAVE_KEY
get_key_security_request Winreg::REG_GET_KEY_SECURITY
set_key_security_request Winreg::REG_SET_KEY_SECURITY
string :default
end
choice 'Netlogon', selection: -> { opnum } do
Expand Down
2 changes: 1 addition & 1 deletion lib/ruby_smb/dcerpc/rrp_rpc_unicode_string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def assign(val)
when BinData::Stringz, BinData::String, String
self.buffer = val.to_s
val_length = val.strip.length
val_length += 1 unless val == ''
val_length += 1# unless val == ''
self.buffer_length = val_length * 2
self.maximum_length = val_length * 2
else
Expand Down
94 changes: 87 additions & 7 deletions lib/ruby_smb/dcerpc/winreg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ module Winreg
REG_CREATE_KEY = 0x06
REG_ENUM_KEY = 0x09
REG_ENUM_VALUE = 0x0a
REG_GET_KEY_SECURITY = 0x0c
REG_OPEN_KEY = 0x0f
REG_QUERY_INFO_KEY = 0x10
REG_QUERY_VALUE = 0x11
REG_SAVE_KEY = 0x14
REG_SET_KEY_SECURITY = 0x15
OPEN_HKCC = 0x1b
OPEN_HKPT = 0x20
OPEN_HKPN = 0x21
Expand All @@ -43,6 +45,10 @@ module Winreg
require 'ruby_smb/dcerpc/winreg/create_key_response'
require 'ruby_smb/dcerpc/winreg/save_key_request'
require 'ruby_smb/dcerpc/winreg/save_key_response'
require 'ruby_smb/dcerpc/winreg/get_key_security_request'
require 'ruby_smb/dcerpc/winreg/get_key_security_response'
require 'ruby_smb/dcerpc/winreg/set_key_security_request'
require 'ruby_smb/dcerpc/winreg/set_key_security_response'

ROOT_KEY_MAP = {
"HKEY_CLASSES_ROOT" => OPEN_HKCR,
Expand Down Expand Up @@ -105,10 +111,11 @@ def open_root_key(root_key)
# @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
def open_key(handle, sub_key)
openkey_request_packet = RubySMB::Dcerpc::Winreg::OpenKeyRequest.new(hkey: handle, lp_sub_key: sub_key)
openkey_request_packet.sam_desired.read_control = 1
openkey_request_packet.sam_desired.key_query_value = 1
openkey_request_packet.sam_desired.key_enumerate_sub_keys = 1
openkey_request_packet.sam_desired.key_notify = 1
#openkey_request_packet.sam_desired.read_control = 1
#openkey_request_packet.sam_desired.key_query_value = 1
#openkey_request_packet.sam_desired.key_enumerate_sub_keys = 1
#openkey_request_packet.sam_desired.key_notify = 1
openkey_request_packet.sam_desired.maximum_allowed = 1
response = dcerpc_request(openkey_request_packet)
begin
open_key_response = RubySMB::Dcerpc::Winreg::OpenKeyResponse.read(response)
Expand All @@ -124,14 +131,14 @@ def open_key(handle, sub_key)
end

# Retrieve the data associated with the named value of a specified
# registry open key.
# registry open key. This will also return the type if required.
#
# @param handle [Ndr::NdrContextHandle] the handle for the key
# @param value_name [String] the name of the value
# @return [String] the data of the value entry
# @raise [RubySMB::Dcerpc::Error::InvalidPacket] if the response is not a QueryValueResponse packet
# @raise [RubySMB::Dcerpc::Error::WinregError] if the response error status is not ERROR_SUCCESS
def query_value(handle, value_name)
def query_value(handle, value_name, type: false)
query_value_request_packet = RubySMB::Dcerpc::Winreg::QueryValueRequest.new(hkey: handle, lp_value_name: value_name)
query_value_request_packet.lp_type = 0
query_value_request_packet.lpcb_data = 0
Expand Down Expand Up @@ -161,7 +168,11 @@ def query_value(handle, value_name)
"#{WindowsError::Win32.find_by_retval(query_value_response.error_status.value).join(',')}"
end

query_value_response.data
if type
[query_value_response.lp_type, query_value_response.data]
else
query_value_response.data
end
end

# Close the handle to the registry key.
Expand Down Expand Up @@ -412,6 +423,75 @@ def enum_registry_values(key, bind: true)
close_key(root_key_handle) if root_key_handle && root_key_handle != subkey_handle
end


def get_key_security(handle, security_information = RubySMB::Dcerpc::Winreg::SecurityInformation::OWNER_SECURITY_INFORMATION)
get_key_security_request = RubySMB::Dcerpc::Winreg::GetKeySecurityRequest.new(
hkey: handle,
security_information: security_information,
prpc_security_descriptor_in: { cb_in_security_descriptor: 4096 }
)
response = dcerpc_request(get_key_security_request)
begin
get_key_security_response = RubySMB::Dcerpc::Winreg::GetKeySecurityResponse.read(response)
rescue IOError
raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the GetKeySecurity response"
end
unless get_key_security_response.error_status == WindowsError::Win32::ERROR_SUCCESS
raise RubySMB::Dcerpc::Error::WinregError, "Error returned when querying information: "\
"#{WindowsError::Win32.find_by_retval(get_key_security_response.error_status.value).join(',')}"
end

get_key_security_response.prpc_security_descriptor_out.lp_security_descriptor.to_a.pack('C*')
end

def get_security_descriptor(key, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION, bind: true)
bind(endpoint: RubySMB::Dcerpc::Winreg) if bind

root_key, sub_key = key.gsub(/\//, '\\').split('\\', 2)
root_key_handle = open_root_key(root_key)
subkey_handle = open_key(root_key_handle, sub_key)
get_key_security(subkey_handle, security_information)
ensure
close_key(subkey_handle) if subkey_handle
close_key(root_key_handle) if root_key_handle
end

def set_key_security(handle, security_descriptor, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION)
set_key_security_request = RubySMB::Dcerpc::Winreg::SetKeySecurityRequest.new(
hkey: handle,
security_information: security_information,
prpc_security_descriptor: {
lp_security_descriptor: security_descriptor.bytes,
cb_in_security_descriptor: security_descriptor.b.size,
cb_out_security_descriptor: security_descriptor.b.size
}
)
response = dcerpc_request(set_key_security_request)
begin
set_key_security_response = RubySMB::Dcerpc::Winreg::SetKeySecurityResponse.read(response)
rescue IOError
raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the SetKeySecurity response"
end
unless set_key_security_response.error_status == WindowsError::Win32::ERROR_SUCCESS
raise RubySMB::Dcerpc::Error::WinregError, "Error returned when setting the registry key: "\
"#{WindowsError::Win32.find_by_retval(set_key_security_response.error_status.value).join(',')}"
end

set_key_security_response.error_status
end

def set_security_descriptor(key, security_descriptor, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION, bind: true)
bind(endpoint: RubySMB::Dcerpc::Winreg) if bind

root_key, sub_key = key.gsub(/\//, '\\').split('\\', 2)
root_key_handle = open_root_key(root_key)
subkey_handle = open_key(root_key_handle, sub_key)
set_key_security(subkey_handle, security_descriptor, security_information)
ensure
close_key(subkey_handle) if subkey_handle
close_key(root_key_handle) if root_key_handle
end

end
end
end
26 changes: 26 additions & 0 deletions lib/ruby_smb/dcerpc/winreg/get_key_security_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module RubySMB
module Dcerpc
module Winreg

# This class represents a GetKeySecurity Request Packet as defined in
# [3.1.5.13 BaseRegGetKeySecurity (Opnum 12)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/b0e1868c-f4fd-4b43-959f-c0f0cac3ee26)
class GetKeySecurityRequest < BinData::Record
attr_reader :opnum

endian :little

rpc_hkey :hkey
uint32 :security_information
rpc_security_descriptor :prpc_security_descriptor_in

def initialize_instance
super
@opnum = REG_GET_KEY_SECURITY
end
end

end
end
end


26 changes: 26 additions & 0 deletions lib/ruby_smb/dcerpc/winreg/get_key_security_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module RubySMB
module Dcerpc
module Winreg

# This class represents a GetKeySecurity Response Packet as defined in
# [3.1.5.13 BaseRegGetKeySecurity (Opnum 12)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/b0e1868c-f4fd-4b43-959f-c0f0cac3ee26)
class GetKeySecurityResponse < BinData::Record
attr_reader :opnum

endian :little

rpc_security_descriptor :prpc_security_descriptor_out
ndr_uint32 :error_status

def initialize_instance
super
@opnum = REG_GET_KEY_SECURITY
end
end

end
end
end



2 changes: 1 addition & 1 deletion lib/ruby_smb/dcerpc/winreg/query_value_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def data
case lp_type
when 1,2
bytes.force_encoding('utf-16le').strip
when 3
when 0,3 # 0 is undefined type, let's consider an array of bytes
bytes
when 4
bytes.unpack('V').first
Expand Down
26 changes: 26 additions & 0 deletions lib/ruby_smb/dcerpc/winreg/set_key_security_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module RubySMB
module Dcerpc
module Winreg

# This class represents a SetKeySecurity Request Packet as defined in
# [3.1.5.21 BaseRegSetKeySecurity (Opnum 21)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/da18856c-8a6d-4217-8e93-3625865e562c)
class SetKeySecurityRequest < BinData::Record
attr_reader :opnum

endian :little

rpc_hkey :hkey
uint32 :security_information
rpc_security_descriptor :prpc_security_descriptor

def initialize_instance
super
@opnum = REG_SET_KEY_SECURITY
end
end

end
end
end


25 changes: 25 additions & 0 deletions lib/ruby_smb/dcerpc/winreg/set_key_security_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module RubySMB
module Dcerpc
module Winreg

# This class represents a SetKeySecurity Response Packet as defined in
# [3.1.5.21 BaseRegSetKeySecurity (Opnum 21)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rrp/da18856c-8a6d-4217-8e93-3625865e562c)
class SetKeySecurityResponse < BinData::Record
attr_reader :opnum

endian :little

ndr_uint32 :error_status

def initialize_instance
super
@opnum = REG_SET_KEY_SECURITY
end
end

end
end
end



17 changes: 17 additions & 0 deletions lib/ruby_smb/field/security_descriptor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@ module Field
# Class representing a SECURITY_DESCRIPTOR as defined in
# [2.4.6 SECURITY_DESCRIPTOR](https://msdn.microsoft.com/en-us/library/cc230366.aspx)
class SecurityDescriptor < BinData::Record

# Security Information as defined in
# [2.4.7 SECURITY_INFORMATION](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/23e75ca3-98fd-4396-84e5-86cd9d40d343)
OWNER_SECURITY_INFORMATION = 0x00000001
GROUP_SECURITY_INFORMATION = 0x00000002
DACL_SECURITY_INFORMATION = 0x00000004
SACL_SECURITY_INFORMATION = 0x00000008
LABEL_SECURITY_INFORMATION = 0x00000010
UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000
PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
ATTRIBUTE_SECURITY_INFORMATION = 0x00000020
SCOPE_SECURITY_INFORMATION = 0x00000040
PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080
BACKUP_SECURITY_INFORMATION = 0x00010000

endian :little
uint8 :revision, label: 'Revision', initial_value: 0x01
uint8 :sbz1, label: 'Resource Manager Control Bits'
Expand Down

0 comments on commit f10e7e8

Please sign in to comment.