Module: Metasploit::Framework::LDAP::Client

Included in:
Metasploit::Framework::LoginScanner::LDAP, Msf::Exploit::Remote::LDAP
Defined in:
lib/metasploit/framework/ldap/client.rb

Instance Method Summary collapse

Instance Method Details

#ldap_connect_opts(rhost, rport, connect_timeout, ssl: true, opts: {}) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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
129
130
131
132
# File 'lib/metasploit/framework/ldap/client.rb', line 8

def ldap_connect_opts(rhost, rport, connect_timeout, ssl: true, opts: {})
  connect_opts = {
    host: rhost,
    port: rport,
    connect_timeout: connect_timeout,
    proxies: opts[:proxies]
  }

  if ssl
    connect_opts[:encryption] = {
      method: :simple_tls,
      tls_options: {
        verify_mode: OpenSSL::SSL::VERIFY_NONE
      }
    }
  end

  case opts[:ldap_auth]
  when Msf::Exploit::Remote::AuthOption::SCHANNEL
    pfx_path = opts[:ldap_cert_file]
    raise Msf::ValidationError, 'The LDAP::CertFile option is required when using SCHANNEL authentication.' if pfx_path.blank?
    raise Msf::ValidationError, 'The SSL option must be enabled when using SCHANNEL authentication.' if ssl != true

    unless ::File.file?(pfx_path) && ::File.readable?(pfx_path)
      raise Msf::ValidationError, 'Failed to load the PFX certificate file. The path was not a readable file.'
    end

    begin
      pkcs = OpenSSL::PKCS12.new(File.binread(pfx_path), '')
    rescue StandardError => e
      raise Msf::ValidationError, "Failed to load the PFX file (#{e})"
    end

    connect_opts[:auth] = {
      method: :sasl,
      mechanism: 'EXTERNAL',
      initial_credential: '',
      challenge_response: true
    }
    connect_opts[:encryption] = {
      method: :start_tls,
      tls_options: {
        verify_mode: OpenSSL::SSL::VERIFY_NONE,
        cert: pkcs.certificate,
        key: pkcs.key
      }
    }
  when Msf::Exploit::Remote::AuthOption::KERBEROS
    raise Msf::ValidationError, 'The Ldap::Rhostname option is required when using Kerberos authentication.' if opts[:ldap_rhostname].blank?
    raise Msf::ValidationError, 'The DOMAIN option is required when using Kerberos authentication.' if opts[:domain].blank?

    offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(opts[:ldap_krb_offered_enc_types])
    raise Msf::ValidationError, 'At least one encryption type is required when using Kerberos authentication.' if offered_etypes.empty?

    kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::LDAP.new(
      host: opts[:domain_controller_rhost].blank? ? nil : opts[:domain_controller_rhost],
      hostname: opts[:ldap_rhostname],
      realm: opts[:domain],
      username: opts[:username],
      password: opts[:password],
      framework: opts[:framework],
      framework_module: opts[:framework_module],
      cache_file: opts[:ldap_krb5_cname].blank? ? nil : opts[:ldap_krb5_cname],
      ticket_storage: opts[:kerberos_ticket_storage],
      offered_etypes: offered_etypes
    )

    connect_opts[:auth] = {
      method: :sasl,
      mechanism: 'GSS-SPNEGO',
      initial_credential: proc do
        kerberos_result = kerberos_authenticator.authenticate
        kerberos_result[:security_blob]
      end,
      challenge_response: true
    }
  when Msf::Exploit::Remote::AuthOption::NTLM
    ntlm_client = RubySMB::NTLM::Client.new(
      opts[:username],
      opts[:password],
      workstation: 'WORKSTATION',
      domain: opts[:domain].blank? ? '.' : opts[:domain],
      flags:
        RubySMB::NTLM::NEGOTIATE_FLAGS[:UNICODE] |
          RubySMB::NTLM::NEGOTIATE_FLAGS[:REQUEST_TARGET] |
          RubySMB::NTLM::NEGOTIATE_FLAGS[:NTLM] |
          RubySMB::NTLM::NEGOTIATE_FLAGS[:ALWAYS_SIGN] |
          RubySMB::NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] |
          RubySMB::NTLM::NEGOTIATE_FLAGS[:KEY_EXCHANGE] |
          RubySMB::NTLM::NEGOTIATE_FLAGS[:TARGET_INFO] |
          RubySMB::NTLM::NEGOTIATE_FLAGS[:VERSION_INFO]
    )

    negotiate = proc do |challenge|
      ntlmssp_offset = challenge.index('NTLMSSP')
      type2_blob = challenge.slice(ntlmssp_offset..-1)
      challenge = [type2_blob].pack('m')
      type3_message = ntlm_client.init_context(challenge)
      type3_message.serialize
    end

    connect_opts[:auth] = {
      method: :sasl,
      mechanism: 'GSS-SPNEGO',
      initial_credential: ntlm_client.init_context.serialize,
      challenge_response: negotiate
    }
  when Msf::Exploit::Remote::AuthOption::PLAINTEXT
    connect_opts[:auth] = {
      method: :simple,
      username: opts[:username],
      password: opts[:password]
    }
  when Msf::Exploit::Remote::AuthOption::AUTO
    unless opts[:username].blank? # plaintext if specified
      connect_opts[:auth] = {
        method: :simple,
        username: opts[:username],
        password: opts[:password]
      }
    end
  end

  connect_opts
end