Module: Msf::Exploit::Remote::DCERPC::KerberosAuthentication

Defined in:
lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb

Overview

This class implements an override for RubySMB’s default authentication method to instead use a kerberos authenticator

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#krb_encryptorObject

Returns the value of attribute krb_encryptor.



134
135
136
# File 'lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb', line 134

def krb_encryptor
  @krb_encryptor
end

Instance Method Details

#auth_provider_complete_handshake(response, options) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb', line 84

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,
                                                                              rc4_pad_style: :eight_byte_aligned)

  # 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

#auth_provider_decrypt_and_verify(dcerpc_response) ⇒ Boolean

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.

Parameters:

  • dcerpc_response (Response)

    The Response packet to decrypt and verify in-place

Returns:

  • (Boolean)

    Is the packet's signature valid?

Raises:

  • ArgumentError If the auth type is not SPNEGO (which ultimately wraps Kerberos)



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb', line 41

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

#auth_provider_encrypt_and_sign(dcerpc_req) ⇒ Object

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.

Parameters:

  • dcerpc_req (Request)

    The Request object to be encrypted and signed in-place



25
26
27
28
29
30
31
32
33
34
# File 'lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb', line 25

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

#auth_provider_initObject

Initialize the auth provider using Kerberos

Returns:

  • Serialized message for initializing the auth provider



15
16
17
18
19
20
# File 'lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb', line 15

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

#build_ap_rep(session_key, sequence_number) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb', line 60

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

#get_auth_padding_length(plaintext_len) ⇒ Object



130
131
132
# File 'lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb', line 130

def get_auth_padding_length(plaintext_len)
  (16 - (self.krb_encryptor.calculate_encrypted_length(plaintext_len) % 16)) % 16
end

#kerberos_authenticator=(kerberos_authenticator) ⇒ Object

Parameters:



9
10
11
# File 'lib/msf/core/exploit/remote/dcerpc/kerberos_authentication.rb', line 9

def kerberos_authenticator=(kerberos_authenticator)
  @kerberos_authenticator = kerberos_authenticator
end