NetExec/cme/protocols/ldap/smbldap.py

182 lines
8.1 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import logging
import random
from pyasn1.codec.der import decoder, encoder
from pyasn1.type.univ import noValue
from impacket.ntlm import compute_lmhash, compute_nthash
from impacket.ldap import ldap as ldap_impacket
from impacket.krb5.kerberosv5 import KerberosError
from cme.logger import CMEAdapter
ldap_error_status = {
"1":"STATUS_NOT_SUPPORTED",
"533":"STATUS_ACCOUNT_DISABLED",
"701":"STATUS_ACCOUNT_EXPIRED",
"531":"STATUS_ACCOUNT_RESTRICTION",
"530":"STATUS_INVALID_LOGON_HOURS",
"532":"STATUS_PASSWORD_EXPIRED",
"773":"STATUS_PASSWORD_MUST_CHANGE",
"775":"USER_ACCOUNT_LOCKED",
"50":"LDAP_INSUFFICIENT_ACCESS",
"KDC_ERR_CLIENT_REVOKED":"KDC_ERR_CLIENT_REVOKED",
"KDC_ERR_PREAUTH_FAILED":"KDC_ERR_PREAUTH_FAILED"
}
class LDAPConnect:
def __init__(self, host, port, hostname):
self.proto_logger(host, port, hostname)
def proto_logger(self, host, port, hostname):
self.logger = CMEAdapter(extra={
'protocol': 'LDAP',
'host': host,
'port': port,
'hostname': hostname
})
def kerberos_login(self, domain, username, password, ntlm_hash, kdc='', aesKey=''):
lmhash = ''
nthash = ''
if kdc == None:
kdc = domain
#This checks to see if we didn't provide the LM Hash
if ntlm_hash and ntlm_hash.find(':') != -1:
lmhash, nthash = ntlm_hash.split(':')
elif ntlm_hash:
nthash = ntlm_hash
# Create the baseDN
baseDN = ''
domainParts = domain.split('.')
for i in domainParts:
baseDN += 'dc=%s,' % i
# Remove last ','
baseDN = baseDN[:-1]
try:
ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % kdc, baseDN)
ldapConnection.kerberosLogin(username, password, domain, lmhash, nthash, aesKey, kdcHost=kdc, useCache=False)
# Connect to LDAP
out = u'{}{}:{}'.format('{}\\'.format(domain),
username,
password if password else ntlm_hash)
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "389"
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
if str(e).find('strongerAuthRequired') >= 0:
# We need to try SSL
try:
ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % kdc, baseDN)
ldapConnection.login(username, password, domain, lmhash, nthash, aesKey, kdcHost=kdc, useCache=False)
self.logger.extra['protocol'] = "LDAPS"
self.logger.extra['port'] = "636"
# self.logger.success(out)
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
errorCode = str(e).split()[-2][:-1]
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
ldap_error_status[errorCode] if errorCode in ldap_error_status else ''),
color='magenta' if errorCode in ldap_error_status else 'red')
else:
errorCode = str(e).split()[-2][:-1]
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
ldap_error_status[errorCode] if errorCode in ldap_error_status else ''),
color='magenta' if errorCode in ldap_error_status else 'red')
return False
except OSError as e:
self.logger.debug(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
"Error connecting to the domain, please add option --kdcHost with the FQDN of the domain controller"))
return False
except KerberosError as e:
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
str(e)),
color='red')
return False
def plaintext_login(self, domain, username, password, ntlm_hash):
lmhash = ''
nthash = ''
#This checks to see if we didn't provide the LM Hash
if ntlm_hash and ntlm_hash.find(':') != -1:
lmhash, nthash = ntlm_hash.split(':')
elif ntlm_hash:
nthash = ntlm_hash
# Create the baseDN
baseDN = ''
domainParts = domain.split('.')
for i in domainParts:
baseDN += 'dc=%s,' % i
# Remove last ','
baseDN = baseDN[:-1]
try:
ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % domain, baseDN, domain)
ldapConnection.login(username, password, domain, lmhash, nthash)
# Connect to LDAP
out = u'{}{}:{}'.format('{}\\'.format(domain),
username,
password if password else ntlm_hash)
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "389"
# self.logger.success(out)
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
print(str(e))
if str(e).find('strongerAuthRequired') >= 0:
# We need to try SSL
try:
ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % domain, baseDN, domain)
ldapConnection.login(username, password, domain, lmhash, nthash)
self.logger.extra['protocol'] = "LDAPS"
self.logger.extra['port'] = "636"
# self.logger.success(out)
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
errorCode = str(e).split()[-2][:-1]
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
ldap_error_status[errorCode] if errorCode in ldap_error_status else ''),
color='magenta' if errorCode in ldap_error_status else 'red')
else:
print(str(e))
errorCode = str(e).split()[-2][:-1]
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
ldap_error_status[errorCode] if errorCode in ldap_error_status else ''),
color='magenta' if errorCode in ldap_error_status else 'red')
return False
except OSError as e:
self.logger.debug(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
"Error connecting to the domain, please add option --kdcHost with the FQDN of the domain controller"))
return False