forked from fortra/impacket
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCheckLDAPStatus.py
More file actions
128 lines (116 loc) · 5.38 KB
/
CheckLDAPStatus.py
File metadata and controls
128 lines (116 loc) · 5.38 KB
1
2
3
4
5
6
7
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
#!/usr/bin/env python
# Impacket - Collection of Python classes for working with network protocols.
#
# Copyright Fortra, LLC and its affiliated companies
#
# All rights reserved.
#
# This software is provided under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description:
# Check LDAP signing status and LDAPS channel binding status.
# First, the script use the given domain controller IP and domain
# name to resolve all the domain controllers. Then the checks are
# performed against all domain controllers.
#
# Author:
# Thomas Seigneuret (@zblurx)
import argparse
import logging
import sys
from dns.resolver import Resolver
from OpenSSL.SSL import SysCallError
from impacket import version
from impacket.examples import logger
from impacket.ldap.ldap import LDAPConnection, LDAPSessionError
class CheckLDAP:
def __init__(self, domain, dc_ip, timeout):
self.domain = domain
self.dc_ip = dc_ip
self.timeout = timeout
def list_dc(self):
dc_list = []
resolver = Resolver()
resolver.timeout = self.timeout
resolver.nameservers = [self.dc_ip]
dc_query = resolver.resolve(
f"_ldap._tcp.dc._msdcs.{self.domain}", 'SRV', tcp=True)
for dc in dc_query:
dc_list.append(str(dc.target).rstrip("."))
return dc_list
def run(self):
dc_list = self.list_dc()
logging.info(f"Found {len(dc_list)} domain controller(s) in {self.domain}")
for dc in dc_list:
signing_required = self.check_ldap_signing(dc)
channel_binding_status = self.check_ldaps_cbt(dc)
print(f"Hostname: {dc}\n\t> LDAP Signing Required: {signing_required}\n\t> LDAPS Channel Binding Status: {channel_binding_status}")
def check_ldaps_cbt(self, hostname):
cbt_status = "Never"
ldap_url = f"ldaps://{hostname}"
try:
ldap_connection = LDAPConnection(url=ldap_url)
ldap_connection.channel_binding_value = None
ldap_connection.login(user=" ", domain=self.domain)
except LDAPSessionError as e:
if str(e).find("data 80090346") >= 0:
cbt_status = "Always" # CBT is Required
# Login failed (wrong credentials). test if we get an error with an existing, but wrong CBT -> When supported
elif str(e).find("data 52e") >= 0:
ldap_connection = LDAPConnection(url=ldap_url)
new_cbv = bytearray(ldap_connection.channel_binding_value)
new_cbv[15] = (new_cbv[3] + 1) % 256
ldap_connection.channel_binding_value = bytes(new_cbv)
try:
ldap_connection.login(user=" ", domain=self.domain)
except LDAPSessionError as e:
if str(e).find("data 80090346") >= 0:
logging.debug(f"LDAPS channel binding is set to 'When Supported' on host {hostname}")
cbt_status = "When Supported" # CBT is When Supported
else:
logging.debug(f"LDAPSessionError while checking for channel binding requirements (likely NTLM disabled): {e!s}")
except SysCallError as e:
logging.debug(f"Received SysCallError when trying to enumerate channel binding support: {e!s}")
if e.args[1] in ["ECONNRESET", "WSAECONNRESET", "Unexpected EOF"]:
cbt_status = "No TLS cert"
else:
raise
return cbt_status
def check_ldap_signing(self, hostname):
signing_required = False
ldap_url = f"ldap://{hostname}"
try:
ldap_connection = LDAPConnection(url=ldap_url, signing=False)
ldap_connection.login(domain=self.domain)
logging.debug(f"LDAP signing is not enforced on {hostname}")
except LDAPSessionError as e:
if str(e).find("strongerAuthRequired") >= 0:
logging.debug(f"LDAP signing is enforced on {hostname}")
signing_required = True
else:
logging.debug(f"LDAPSessionError while checking for signing requirements (likely NTLM disabled): {e!s}")
return signing_required
if __name__ == '__main__':
print(version.BANNER)
parser = argparse.ArgumentParser(add_help = True, description = "LDAP signing and channel binding enumeration utility.")
parser.add_argument('-dc-ip', required=True, action='store', metavar="ip address", help='IP Address of a domain controller or a DNS resolver for the domain.')
parser.add_argument('-domain', required=True, action='store', help='<domain name>')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-timeout', action='store', type=int, default=15, help='DNS timeout')
parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
logger.init(options.ts, options.debug)
try:
dumper = CheckLDAP(options.domain, options.dc_ip, options.timeout)
logging.info(f"Targeted domain: {options.domain}")
dumper.run()
except Exception as e:
if logging.getLogger().level == logging.DEBUG:
import traceback
traceback.print_exc()
logging.error(str(e))