From a5934c5889baac68b42e9aaccb4f73fe94963586 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 7 Feb 2025 15:34:20 -0500 Subject: [PATCH 1/3] Initial addition of GKDI GetKey --- lib/ruby_smb/dcerpc.rb | 1 + lib/ruby_smb/dcerpc/error.rb | 10 ++++ lib/ruby_smb/dcerpc/gkdi.rb | 52 +++++++++++++++++++ .../dcerpc/gkdi/gkdi_get_key_request.rb | 26 ++++++++++ .../dcerpc/gkdi/gkdi_get_key_response.rb | 23 ++++++++ .../dcerpc/gkdi/gkdi_group_key_envelope.rb | 51 ++++++++++++++++++ lib/ruby_smb/dcerpc/request.rb | 3 ++ 7 files changed, 166 insertions(+) create mode 100644 lib/ruby_smb/dcerpc/gkdi.rb create mode 100644 lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_request.rb create mode 100644 lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_response.rb create mode 100644 lib/ruby_smb/dcerpc/gkdi/gkdi_group_key_envelope.rb diff --git a/lib/ruby_smb/dcerpc.rb b/lib/ruby_smb/dcerpc.rb index 682abcda7..660c16c82 100644 --- a/lib/ruby_smb/dcerpc.rb +++ b/lib/ruby_smb/dcerpc.rb @@ -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' diff --git a/lib/ruby_smb/dcerpc/error.rb b/lib/ruby_smb/dcerpc/error.rb index 92a96a084..d2bff7cc6 100644 --- a/lib/ruby_smb/dcerpc/error.rb +++ b/lib/ruby_smb/dcerpc/error.rb @@ -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 diff --git a/lib/ruby_smb/dcerpc/gkdi.rb b/lib/ruby_smb/dcerpc/gkdi.rb new file mode 100644 index 000000000..a898cbd81 --- /dev/null +++ b/lib/ruby_smb/dcerpc/gkdi.rb @@ -0,0 +1,52 @@ +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_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 diff --git a/lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_request.rb b/lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_request.rb new file mode 100644 index 000000000..022d295aa --- /dev/null +++ b/lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_request.rb @@ -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 diff --git a/lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_response.rb b/lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_response.rb new file mode 100644 index 000000000..da186b64f --- /dev/null +++ b/lib/ruby_smb/dcerpc/gkdi/gkdi_get_key_response.rb @@ -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 diff --git a/lib/ruby_smb/dcerpc/gkdi/gkdi_group_key_envelope.rb b/lib/ruby_smb/dcerpc/gkdi/gkdi_group_key_envelope.rb new file mode 100644 index 000000000..588ec3776 --- /dev/null +++ b/lib/ruby_smb/dcerpc/gkdi/gkdi_group_key_envelope.rb @@ -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 diff --git a/lib/ruby_smb/dcerpc/request.rb b/lib/ruby_smb/dcerpc/request.rb index 32d28f9dd..1829e3a52 100644 --- a/lib/ruby_smb/dcerpc/request.rb +++ b/lib/ruby_smb/dcerpc/request.rb @@ -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 From 2ea608e18ca7c36862ab3e696c306156d5e031fa Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 21 Feb 2025 16:51:37 -0500 Subject: [PATCH 2/3] Add the GkdiFfcDhKey class --- lib/ruby_smb/dcerpc/gkdi.rb | 1 + lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_key.rb | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_key.rb diff --git a/lib/ruby_smb/dcerpc/gkdi.rb b/lib/ruby_smb/dcerpc/gkdi.rb index a898cbd81..a30d576ca 100644 --- a/lib/ruby_smb/dcerpc/gkdi.rb +++ b/lib/ruby_smb/dcerpc/gkdi.rb @@ -12,6 +12,7 @@ module Gkdi 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_group_key_envelope' def gkdi_get_key(target_sd, root_key_id, l0_key_id, l1_key_id, l2_key_id) diff --git a/lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_key.rb b/lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_key.rb new file mode 100644 index 000000000..88b383eb8 --- /dev/null +++ b/lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_key.rb @@ -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 From 2071a8393293d18c76a78a900a0cbbbb8f69099a Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Tue, 25 Feb 2025 15:41:16 -0500 Subject: [PATCH 3/3] Add the GkdiFfcDhParameters class --- lib/ruby_smb/dcerpc/gkdi.rb | 1 + .../dcerpc/gkdi/gkdi_ffc_dh_parameters.rb | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_parameters.rb diff --git a/lib/ruby_smb/dcerpc/gkdi.rb b/lib/ruby_smb/dcerpc/gkdi.rb index a30d576ca..87aa01077 100644 --- a/lib/ruby_smb/dcerpc/gkdi.rb +++ b/lib/ruby_smb/dcerpc/gkdi.rb @@ -13,6 +13,7 @@ module Gkdi 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) diff --git a/lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_parameters.rb b/lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_parameters.rb new file mode 100644 index 000000000..3d7db168b --- /dev/null +++ b/lib/ruby_smb/dcerpc/gkdi/gkdi_ffc_dh_parameters.rb @@ -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