Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GetKeySecurity and SetKeySecurity MS-RRP structures (DCERPC) #265

Merged
merged 2 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions examples/registry_key_security_descriptor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/ruby

# This example script is used for testing the Winreg registry key security descriptor functionalities.
# It will attempt to connect to a host and reads (or writes) the security descriptor of a specified registry key.
#
# Example usage:
# - read:
# ruby examples/read_registry_key_security.rb --username msfadmin --password msfadmin -i 7 -o r 192.168.172.138 'HKLM\SECURITY\Policy\PolEKList'
# This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin
# credentialas and read the security descriptor of the
# `HKLM\SECURITY\Policy\PolEKList` registry key with the security information 7
# (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
# DACL_SECURITY_INFORMATION).
#
# - write:
# 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'
# This will try to connect to \\192.168.172.138 with the msfadmin:msfadmin
# credentialas and write the given security descriptor to the
# `HKLM\SECURITY\Policy\PolEKList` registry key with the security information 4
# (DACL_SECURITY_INFORMATION).

require 'bundler/setup'
require 'optparse'
require 'ruby_smb'

OPERATIONS = %w{read write}
OPERATION_ALIASES = { "r" => "read", "w" => "write" }

args = ARGV.dup
options = {
domain: '.',
username: '',
password: '',
smbv1: true,
smbv2: true,
smbv3: true,
target: nil,
key: nil,
operation: 'read',
info: RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION | RubySMB::Field::SecurityDescriptor::GROUP_SECURITY_INFORMATION | RubySMB::Field::SecurityDescriptor::DACL_SECURITY_INFORMATION,
sd: nil
}
options[:key] = args.pop
options[:target ] = args.pop
optparser = OptionParser.new do |opts|
opts.banner = "Usage: #{File.basename(__FILE__)} [options] target reg_key"
opts.on('--[no-]smbv1', "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
options[:smbv1] = smbv1
end
opts.on('--[no-]smbv2', "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
options[:smbv2] = smbv2
end
opts.on('--[no-]smbv3', "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
options[:smbv3] = smbv3
end
opts.on('-u', '--username [USERNAME]', "The account's username (default: #{options[:username]})") do |username|
if username.include?('\\')
options[:domain], options[:username] = username.split('\\', 2)
else
options[:username] = username
end
end
opts.on('-p', '--password [PASSWORD]', "The account's password (default: #{options[:password]})") do |password|
options[:password] = password
end
operation_list = (OPERATION_ALIASES.keys + OPERATIONS).join(', ')
opts.on('-o', '--operation OPERATION', OPERATIONS, OPERATION_ALIASES, "The operation to perform on the registry key (default: #{options[:operation]})", "(#{operation_list})") do |operation|
options[:operation] = operation
end
opts.on('-i', '--info [SECURITY INFORMATION]', Integer, "The security information value (default: #{options[:info]})") do |password|
options[:info] = password
end
opts.on('-s', '--sd [SECURITY DESCRIPTOR]', "The security descriptor to write as an hex string") do |sd|
options[:sd] = sd
end
end
optparser.parse!(args)

if options[:target].nil? || options[:key].nil?
abort(optparser.help)
end

sock = TCPSocket.new options[:target], 445
dispatcher = RubySMB::Dispatcher::Socket.new(sock)

client = RubySMB::Client.new(dispatcher, smb1: options[:smbv1], smb2: options[:smbv2], smb3: options[:smbv3], username: options[:username], password: options[:password], domain: options[:domain])
protocol = client.negotiate
status = client.authenticate

puts "#{protocol}: #{status}"

case options[:operation]
when 'read', 'r'
puts "Read registry key #{options[:key]} security descriptor with security information #{options[:info]}"
security_descriptor = client.get_key_security_descriptor(options[:target], options[:key], options[:info])
puts "Security descriptor: #{security_descriptor.b.bytes.map {|c| "%02x" % c.ord}.join}"
when 'write', 'w'
unless options[:sd] && !options[:sd].empty?
puts "Security descriptor missing"
abort(optparser.help)
end
puts "Write security descriptor #{options[:sd]} to registry key #{options[:key]} with security information #{options[:info]}"
sd = options[:sd].chars.each_slice(2).map {|c| c.join.to_i(16).chr}.join
status = client.set_key_security_descriptor(options[:target], options[:key], sd, options[:info])
puts "Success!"
end

client.disconnect!

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_key_security_descriptor(host, key, security_information = RubySMB::Field::SecurityDescriptor::OWNER_SECURITY_INFORMATION)
connect_to_winreg(host) do |named_pipe|
named_pipe.get_key_security_descriptor(key, security_information)
end
end

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

end
end
end
Expand Down
14 changes: 10 additions & 4 deletions lib/ruby_smb/dcerpc/ndr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -563,8 +563,11 @@ def do_num_bytes
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
# Add one to count the terminator. According to
# https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02,
# the NDR String must contain at least one element, the terminator. So,
# add one even if it is an empty string.
max_count += 1
return max_count
else
return val.to_s.length
Expand Down Expand Up @@ -618,8 +621,11 @@ def do_num_bytes
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
# Add one to count the terminator. According to
# https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04,
# the NDR String must contain at least one element, the terminator. So,
# add one even if it is an empty string.
@actual_count += 1
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
self.buffer_length = val_length * 2
self.maximum_length = val_length * 2
else
Expand Down
Loading
Loading