Module: Msf::Exploit::Remote::SMB::Client::KerberosAuthentication

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

Overview

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

Instance Method Summary collapse

Instance Method Details

#authenticateObject

Raises:

  • (::RubySMB::Error::AuthenticationFailure)


13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/msf/core/exploit/remote/smb/client/kerberos_authentication.rb', line 13

def authenticate
  raise ::RubySMB::Error::AuthenticationFailure, "Missing negotiation security buffer" if negotiation_security_buffer.nil?

  gss_api = OpenSSL::ASN1.decode(negotiation_security_buffer)
  mech_types = RubySMB::Gss.asn1dig(gss_api, 1, 0, 0, 0)&.value || []
  has_kerberos_gss_mech_type = mech_types&.any? { |mech_type| mech_type.value == ::Rex::Proto::Gss::OID_MICROSOFT_KERBEROS_5.value }

  error = "Unable to negotiate kerberos with the remote host. Expected oid #{::Rex::Proto::Gss::OID_MICROSOFT_KERBEROS_5.value} in #{mech_types.map(&:value).inspect}"
  raise ::RubySMB::Error::AuthenticationFailure, error unless has_kerberos_gss_mech_type

  if smb1
    smb1_authenticate
  else
    smb2_authenticate
  end
end

#kerberos_authenticator=(kerberos_authenticator) ⇒ Object

Parameters:



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

def kerberos_authenticator=(kerberos_authenticator)
  @kerberos_authenticator = kerberos_authenticator
end

#smb1_authenticateObject

Handles SMB1 Kerberos Authentication by delegating to a kerberos_authenticator implementation to generate a GSS security blob with an embedded AP_REQ. On success information is stored about the peer/server.

Raises:

  • (::RubySMB::Error::AuthenticationFailure)


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/msf/core/exploit/remote/smb/client/kerberos_authentication.rb', line 37

def smb1_authenticate
  raise ::RubySMB::Error::AuthenticationFailure, 'Missing kerberos authenticator' unless @kerberos_authenticator

  kerberos_result = @kerberos_authenticator.authenticate

  @application_key = @session_key = kerberos_result[:session_key].value[0...16]

  raw_kerberos_response = smb1_kerberos_authenticate(kerberos_result[:security_blob])
  response = smb1_session_setup_response(raw_kerberos_response)
  @kerberos_authenticator.validate_response!(response.data_block.security_blob)

  response_code = response.status_code

  # Store the available OS information before going forward.
  @peer_native_os = response.data_block.native_os.to_s
  @peer_native_lm = response.data_block.native_lan_man.to_s

  @user_id = response.smb_header.uid if response_code == WindowsError::NTStatus::STATUS_SUCCESS

  response_code
end

#smb1_kerberos_authenticate(security_buffer) ⇒ String

Returns the raw binary response from the server.

Parameters:

  • type3_message (String)

    the NTLM Type 3 message

  • user_id (Integer)

    the temporary user ID from the Type 2 response

Returns:

  • (String)

    the raw binary response from the server



63
64
65
66
# File 'lib/msf/core/exploit/remote/smb/client/kerberos_authentication.rb', line 63

def smb1_kerberos_authenticate(security_buffer)
  packet = smb1_kerberos_authenticate_packet(security_buffer)
  send_recv(packet)
end

#smb1_kerberos_authenticate_packet(security_blob) ⇒ RubySMB::SMB1::Packet::SessionSetupRequest

Generates the RubySMB::SMB1::Packet::SessionSetupRequest packet with the NTLM Type 3 (Auth) message in the security_blob field.

Parameters:

  • type3_message (String)

    the NTLM Type 3 message

  • user_id (Integer)

    the temporary user ID from the Type 2 response

Returns:

  • (RubySMB::SMB1::Packet::SessionSetupRequest)

    the second authentication packet to send



74
75
76
77
78
79
80
81
82
# File 'lib/msf/core/exploit/remote/smb/client/kerberos_authentication.rb', line 74

def smb1_kerberos_authenticate_packet(security_blob)
  packet = RubySMB::SMB1::Packet::SessionSetupRequest.new
  # packet.smb_header.uid = user_id
  packet.set_security_buffer(security_blob)
  packet.parameter_block.max_buffer_size = self.max_buffer_size
  packet.parameter_block.max_mpx_count = 50
  packet.smb_header.flags2.extended_security = 1
  packet
end

#smb2_authenticateObject

Handles SMB2 Kerberos Authentication by delegating to a kerberos_authenticator implementation to generate a GSS security blob with an embedded AP_REQ. On success information is stored about the peer/server.

Raises:

  • (::RubySMB::Error::AuthenticationFailure)


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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/msf/core/exploit/remote/smb/client/kerberos_authentication.rb', line 91

def smb2_authenticate
  raise ::RubySMB::Error::AuthenticationFailure, 'Missing kerberos authenticator' unless @kerberos_authenticator

  kerberos_result = @kerberos_authenticator.authenticate

  raw_kerberos_response = smb2_kerberos_authenticate(kerberos_result[:security_blob])
  response = smb2_session_setup_response(raw_kerberos_response)
  @kerberos_authenticator.validate_response!(response.buffer)

  @session_id = response.smb2_header.session_id
  if @encryption_algorithm.present?
    key_len = OpenSSL::Cipher.new(@encryption_algorithm).key_len
  else
    key_len = 16
  end
  @application_key = @session_key = kerberos_result[:session_key].value[0...key_len]

  @session_is_guest = response.session_flags.guest == 1

  if @smb3
    if response.session_flags.encrypt_data == 1
      # if the server indicates that encryption is required, enable it
      @session_encrypt_data = true
    elsif (@session_is_guest && password != '') || (username == '' && password == '')
      # disable encryption when necessary
      @session_encrypt_data = false
    end

    # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/7fd079ca-17e6-4f02-8449-46b606ea289c
    if @dialect == '0x0300' || @dialect == '0x0302'
      @application_key = RubySMB::Crypto::KDF.counter_mode(
        @session_key,
        "SMB2APP\x00",
        "SmbRpc\x00"
      )
    else
      @application_key = RubySMB::Crypto::KDF.counter_mode(
        @session_key,
        "SMBAppKey\x00",
        @preauth_integrity_hash_value
      )
    end
    # otherwise, leave encryption to the default value that it was initialized to
  end
  ######
  # DEBUG
  #puts "Session ID = #{@session_id.to_binary_s.each_byte.map {|e| '%02x' % e}.join}"
  #puts "Session key = #{@session_key.each_byte.map {|e| '%02x' % e}.join}"
  #puts "PreAuthHash = #{@preauth_integrity_hash_value.each_byte.map {|e| '%02x' % e}.join}" if @preauth_integrity_hash_value
  ######

  response.status_code
end

#smb2_kerberos_authenticate(security_blob) ⇒ Object



152
153
154
155
156
157
158
159
# File 'lib/msf/core/exploit/remote/smb/client/kerberos_authentication.rb', line 152

def smb2_kerberos_authenticate(security_blob)
  packet = smb2_kerberos_authenticate_packet(security_blob)
  response = send_recv(packet)
  if @dialect == '0x0311'
    update_preauth_hash(packet)
  end
  response
end

#smb2_kerberos_authenticate_packet(security_blob) ⇒ Object



145
146
147
148
149
150
# File 'lib/msf/core/exploit/remote/smb/client/kerberos_authentication.rb', line 145

def smb2_kerberos_authenticate_packet(security_blob)
  packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
  packet.set_security_buffer(security_blob)
  packet.security_mode.signing_enabled = 1
  packet
end