Skip to content
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
1 change: 1 addition & 0 deletions lib/ruby_smb/dcerpc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ module Dcerpc
require 'ruby_smb/dcerpc/icpr'
require 'ruby_smb/dcerpc/efsrpc'
require 'ruby_smb/dcerpc/lsarpc'
require 'ruby_smb/dcerpc/gkdi'
require 'ruby_smb/dcerpc/request'
require 'ruby_smb/dcerpc/response'
require 'ruby_smb/dcerpc/rpc_auth3'
Expand Down
10 changes: 10 additions & 0 deletions lib/ruby_smb/dcerpc/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ def initialize(msg, status_code: nil)
super(msg)
end
end

class GkdiError < DcerpcError
include RubySMB::Error::UnexpectedStatusCode::Mixin

def initialize(msg, status_code: nil)
self.status_code = status_code unless status_code.nil?

super(msg)
end
end
end
end
end
54 changes: 54 additions & 0 deletions lib/ruby_smb/dcerpc/gkdi.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module RubySMB
module Dcerpc
module Gkdi

# [2.1 Transport](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gkdi/2ca63ad2-2464-4a41-ba84-2e0270e95e86)
UUID = 'b9785960-524f-11df-8b6d-83dcded72085'
VER_MAJOR = 1
VER_MINOR = 0

# Operation numbers
GKDI_GET_KEY = 0x0000

require 'ruby_smb/dcerpc/gkdi/gkdi_get_key_request'
require 'ruby_smb/dcerpc/gkdi/gkdi_get_key_response'
require 'ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_key'
require 'ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_parameters'
require 'ruby_smb/dcerpc/gkdi/gkdi_group_key_envelope'

def gkdi_get_key(target_sd, root_key_id, l0_key_id, l1_key_id, l2_key_id)
target_sd = target_sd.to_binary_s if target_sd.respond_to?(:to_binary_s)

gkdi_get_key_request = GkdiGetKeyRequest.new(
cb_target_sd: target_sd.length,
pb_target_sd: target_sd.unpack('C*'),
p_root_key_id: root_key_id,
l0_key_id: l0_key_id,
l1_key_id: l1_key_id,
l2_key_id: l2_key_id
)

response = dcerpc_request(
gkdi_get_key_request,
auth_level: @auth_level,
auth_type: @auth_type
)
begin
gkdi_get_key_response = GkdiGetKeyResponse.read(response)
rescue IOError
raise RubySMB::Dcerpc::Error::InvalidPacket, 'Error reading CertServerRequestResponse'
end
unless gkdi_get_key_response.error_status == WindowsError::NTStatus::STATUS_SUCCESS
status_code = WindowsError::Win32.find_by_retval(gkdi_get_key_response.error_status.value).first
raise RubySMB::Dcerpc::Error::GkdiError.new(
"Error returned with gkdi_get_key: #{status_code}",
status_code: status_code
)
end

GkdiGroupKeyEnvelope.read(gkdi_get_key_response.pbb_out.snapshot.pack('C*'))
end

end
end
end
17 changes: 17 additions & 0 deletions lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module RubySMB
module Dcerpc
module Gkdi

# [2.2.3.1 FFC DH Key](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gkdi/f8770f01-036d-4bf6-a4cf-1bd0e3913404)
class GkdiFfcDhKey < BinData::Record
endian :little

uint8_array :magic, initial_length: 4, initial_value: [ 0x44, 0x48, 0x50, 0x42 ]
uint32 :key_length
uint8_array :field_order, initial_length: :key_length
uint8_array :generator, initial_length: :key_length
uint8_array :public_key, initial_length: :key_length
end
end
end
end
17 changes: 17 additions & 0 deletions lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_parameters.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module RubySMB
module Dcerpc
module Gkdi

# [2.2.2 FFC DH Parameters](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gkdi/e15ae269-ee21-446a-a480-de3ea243db5f)
class GkdiFfcDhParameters < BinData::Record
endian :little

uint32 :parameters_length, initial_value: -> { (key_length * 2) + offset_of(generator) }
uint8_array :magic, initial_length: 4, initial_value: [ 0x44, 0x48, 0x50, 0x4d ]
uint32 :key_length
uint8_array :field_order, initial_length: :key_length
uint8_array :generator, initial_length: :key_length
end
end
end
end
26 changes: 26 additions & 0 deletions lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module RubySMB
module Dcerpc
module Gkdi

# [3.1.4.1 GetKey (Opnum 0)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gkdi/4cac87a3-521e-4918-a272-240f8fabed39)
class GkdiGetKeyRequest < BinData::Record
attr_reader :opnum

endian :little

ndr_uint32 :cb_target_sd
ndr_conf_array :pb_target_sd, type: :ndr_uint8
uuid_ptr :p_root_key_id
ndr_int32 :l0_key_id
ndr_int32 :l1_key_id
ndr_int32 :l2_key_id

def initialize_instance
super
@opnum = GKDI_GET_KEY
end
end

end
end
end
23 changes: 23 additions & 0 deletions lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module RubySMB
module Dcerpc
module Gkdi

# [3.1.4.1 GetKey (Opnum 0)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gkdi/4cac87a3-521e-4918-a272-240f8fabed39)
class GkdiGetKeyResponse < BinData::Record
attr_reader :opnum

endian :little

ndr_uint32 :pcb_out
ndr_byte_conf_array_ptr :pbb_out
ndr_uint32 :error_status

def initialize_instance
super
@opnum = GKDI_GET_KEY
end
end

end
end
end
51 changes: 51 additions & 0 deletions lib/ruby_smb/dcerpc/gkdi/gkdi_group_key_envelope.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
module RubySMB
module Dcerpc
module Gkdi

# [2.2.4 Group Key Envelope](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gkdi/192c061c-e740-4aa0-ab1d-6954fb3e58f7)
class GkdiGroupKeyEnvelope < BinData::Record
endian :little

uint32 :version
uint8_array :magic, initial_length: 4, initial_value: [ 0x4b, 0x44, 0x53, 0x5b ]
uint32 :dw_flags
uint32 :l0_index
uint32 :l1_index
uint32 :l2_index
uuid :root_key_identifier
uint32 :cb_kdf_algorithm
uint32 :cb_kdf_parameters, initial_value: -> { kdf_parameters.length }
uint32 :cb_secret_agreement_algorithm
uint32 :cb_secret_agreement_parameters
uint32 :private_key_length
uint32 :public_key_length
uint32 :cb_l1_key
uint32 :cb_l2_key
uint32 :cb_domain_name
uint32 :cb_forest_name
stringz16 :kdf_algorithm
struct :kdf_parameters, only_if: -> { cb_kdf_parameters > 0 } do
uint8_array :block0, initial_length: 8, initial_value: [ 0, 0, 0, 0, 1, 0, 0, 0 ]
uint32 :length_of_hash_name, initial_value: -> { hash_algorithm_name.length }
uint8_array :block1, initial_length: 4, initial_value: [ 0, 0, 0, 0 ]
stringz16 :hash_algorithm_name
end
stringz16 :secret_agreement_algorithm
uint8_array :secret_agreement_parameters, initial_length: :cb_secret_agreement_parameters
stringz16 :domain_name
stringz16 :forest_name
uint8_array :l1_key, initial_length: 64, only_if: -> { cb_l1_key != 0 }
uint8_array :l2_key, initial_length: :l2_key_length, only_if: -> { cb_l2_key != 0 }

private

def l2_key_length
return 0 if cb_l2_key == 0
return 64 if (dw_flags & (1 << 31)) == 0

public_key_length
end
end
end
end
end
3 changes: 3 additions & 0 deletions lib/ruby_smb/dcerpc/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ class Request < BinData::Record
lsar_close_handle_request Lsarpc::LSAR_CLOSE_HANDLE
lsar_lookup_sids_request Lsarpc::LSAR_LOOKUP_SIDS
end
choice 'Gkdi', selection: -> { opnum } do
gkdi_get_key_request Gkdi::GKDI_GET_KEY
end
string :default
end

Expand Down
Loading