-
Notifications
You must be signed in to change notification settings - Fork 14.1k
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
DCSync using Kerberos #18419
Merged
Merged
DCSync using Kerberos #18419
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
1bd2290
Support Kerberos auth for DCERPC
smashery 587c327
Correctly align sec trailer and stub along a 16-byte boundary
smashery 185cba0
Support validating partial handshakes
smashery 1071341
Changes from code review
smashery 776c064
Corresponding change from RubySMB code review
smashery 235009d
Use the new AlterContext definition
smcintyre-r7 0b7f079
Bump RubySMB to 3.2.6
smcintyre-r7 2a699b8
Changes from code review
smashery File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
# -*- coding: binary -*- | ||
|
||
# | ||
# This class implements an override for RubySMB's default authentication method to instead | ||
# use a kerberos authenticator | ||
# | ||
module Msf::Exploit::Remote::DCERPC::KerberosAuthentication | ||
# @param [Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::SMB] kerberos_authenticator The authenticator to make the required Kerberos requests | ||
def kerberos_authenticator=(kerberos_authenticator) | ||
@kerberos_authenticator = kerberos_authenticator | ||
end | ||
|
||
# Initialize the auth provider using Kerberos | ||
# @return Serialized message for initializing the auth provider | ||
def auth_provider_init | ||
kerberos_result = @kerberos_authenticator.authenticate | ||
@application_key = @session_key = kerberos_result[:session_key] | ||
@client_sequence_number = kerberos_result[:client_sequence_number] | ||
kerberos_result[:security_blob] | ||
end | ||
|
||
# Encrypt the value in dcerpc_req, and add a valid signature to the request. | ||
# This function modifies the request object in-place, and does not return anything. | ||
# @param dcerpc_req [Request] The Request object to be encrypted and signed in-place | ||
def auth_provider_encrypt_and_sign(dcerpc_req) | ||
auth_pad_length = get_auth_padding_length(dcerpc_req.stub.to_binary_s.length) | ||
plain_stub = dcerpc_req.stub.to_binary_s + "\x00" * auth_pad_length | ||
emessage, header_length, krb_pad_length = self.krb_encryptor.encrypt_and_increment(plain_stub) | ||
|
||
encrypted_stub = emessage[header_length..-1] | ||
signature = emessage[0,header_length] | ||
set_encrypted_packet(dcerpc_req, encrypted_stub, auth_pad_length) | ||
set_signature_on_packet(dcerpc_req, signature) | ||
end | ||
|
||
# Decrypt the value in dcerpc_response, and validate its signature. | ||
# This function modifies the request object in-place, and returns whether the signature was valid. | ||
# @param dcerpc_response [Response] The Response packet to decrypt and verify in-place | ||
# @raise ArgumentError If the auth type is not SPNEGO (which ultimately wraps Kerberos) | ||
# @return [Boolean] Is the packet's signature valid? | ||
def auth_provider_decrypt_and_verify(dcerpc_response) | ||
auth_type = dcerpc_response.sec_trailer.auth_type | ||
unless [RubySMB::Dcerpc::RPC_C_AUTHN_GSS_NEGOTIATE].include?(auth_type) | ||
raise ArgumentError, "Unsupported Auth Type: #{dcerpc_response.sec_trailer.auth_type}" | ||
end | ||
encrypted_stub = get_response_full_stub(dcerpc_response) | ||
signature = dcerpc_response.auth_value | ||
data = signature + encrypted_stub | ||
|
||
begin | ||
result = self.krb_encryptor.decrypt_and_verify(data) | ||
rescue Rex::Proto::Kerberos::Model::Error::KerberosError | ||
return false | ||
end | ||
set_decrypted_packet(dcerpc_response, result) | ||
|
||
true | ||
end | ||
|
||
def build_ap_rep(session_key, sequence_number) | ||
pvno = Rex::Proto::Kerberos::Model::VERSION | ||
msg_type = Rex::Proto::Kerberos::Model::AP_REP | ||
ctime = Time.now.utc | ||
cusec = ctime&.usec | ||
|
||
encrypted_part = Rex::Proto::Kerberos::Model::EncApRepPart.new( | ||
ctime: ctime, | ||
cusec: cusec, | ||
sequence_number: sequence_number, | ||
enc_key_usage: Rex::Proto::Kerberos::Crypto::KeyUsage::AP_REP_ENCPART | ||
) | ||
enc_aprep = Rex::Proto::Kerberos::Model::EncryptedData.new( | ||
etype: session_key.type, | ||
cipher: encrypted_part.encrypt(session_key.type, session_key.value) | ||
) | ||
|
||
Rex::Proto::Kerberos::Model::ApRep.new( | ||
pvno: pvno, | ||
msg_type: msg_type, | ||
enc_part: enc_aprep | ||
) | ||
end | ||
|
||
def auth_provider_complete_handshake(response, options) | ||
begin | ||
@kerberos_authenticator.validate_response!(response.auth_value, accept_incomplete: true) | ||
gss_api = OpenSSL::ASN1.decode(response.auth_value) | ||
security_blob = ::RubySMB::Gss.asn1dig(gss_api, 0, 2, 0)&.value | ||
ap_rep = Rex::Proto::Kerberos::Model::ApRep.decode(security_blob) | ||
ap_rep_enc_part = ap_rep.decrypt_enc_part(@session_key.value) | ||
rescue ::Rex::Proto::Kerberos::Model::Error::KerberosDecodingError, | ||
::Rex::Proto::Kerberos::Model::Error::KerberosError, | ||
OpenSSL::ASN1::ASN1Error => e | ||
raise RubySMB::Dcerpc::Error::BindError, e.message # raise the more context-specific BindError | ||
end | ||
server_sequence_number = ap_rep_enc_part.sequence_number | ||
# Now complete the handshake - see [MS-KILE] 3.4.5.1 - https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/190ab8de-dc42-49cf-bf1b-ea5705b7a087 | ||
response_ap_rep = build_ap_rep(@session_key, server_sequence_number) | ||
|
||
wrapped_ap_rep = OpenSSL::ASN1::ASN1Data.new([ | ||
OpenSSL::ASN1::Sequence.new([ | ||
OpenSSL::ASN1::ASN1Data.new([ | ||
OpenSSL::ASN1::OctetString(response_ap_rep.encode) | ||
], 2, :CONTEXT_SPECIFIC) | ||
]) | ||
], 1, :CONTEXT_SPECIFIC).to_der | ||
|
||
alter_ctx = RubySMB::Dcerpc::AlterContext.new(options) | ||
alter_ctx.pdu_header.call_id = @call_id | ||
|
||
add_auth_verifier(alter_ctx, wrapped_ap_rep) | ||
|
||
send_packet(alter_ctx) | ||
|
||
begin | ||
dcerpc_response = recv_struct(RubySMB::Dcerpc::AlterContextResp) | ||
rescue RubySMB::Dcerpc::Error::InvalidPacket | ||
raise RubySMB::Dcerpc::Error::BindError, e.message # raise the more context-specific BindError | ||
end | ||
|
||
self.krb_encryptor = @kerberos_authenticator.get_message_encryptor(ap_rep_enc_part.subkey, | ||
@client_sequence_number, | ||
server_sequence_number) | ||
# Set the session key value on the parent class - needed for decrypting attribute values in e.g. DRSR | ||
@session_key = ap_rep_enc_part.subkey.value | ||
end | ||
|
||
def get_auth_padding_length(plaintext_len) | ||
(16 - (self.krb_encryptor.calculate_encrypted_length(plaintext_len) % 16)) % 16 | ||
end | ||
|
||
attr_accessor :krb_encryptor | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand this correctly,
self.krb_encryptor.decrypt_and_verify
should only verify if the decrypted data is correct (checksum, sequence number, etc.). I believe the signature in the DCERPC response is not verified. This method is supposed to also verify the signature and return a boolean.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
decrypt_and_verify
passes off to the respective Kerberos encryption routines to do the decryption+verification. These throw exceptions if it's invalid (error message will be "HMAC integrity error". So therescue
above will catch that and returnfalse
. But good catch - function needs to returntrue
if it passes through without error.