Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/servers/socksplugins/smtp.py : 13%

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) 2018 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# A Socks Proxy for the SMTP Protocol
11#
12# A simple SOCKS server that proxies a connection to relayed SMTP connections
13#
14# Author:
15# Dirk-jan Mollema (@_dirkjan) / Fox-IT (https://www.fox-it.com)
16#
17import base64
19from impacket import LOG
20from impacket.examples.ntlmrelayx.servers.socksserver import SocksRelay
22# Besides using this base class you need to define one global variable when
23# writing a plugin:
24PLUGIN_CLASS = "SMTPSocksRelay"
25EOL = '\r\n'
27class SMTPSocksRelay(SocksRelay):
28 PLUGIN_NAME = 'SMTP Socks Plugin'
29 PLUGIN_SCHEME = 'SMTP'
31 def __init__(self, targetHost, targetPort, socksSocket, activeRelays):
32 SocksRelay.__init__(self, targetHost, targetPort, socksSocket, activeRelays)
33 self.packetSize = 8192
35 @staticmethod
36 def getProtocolPort():
37 return 25
39 def getServerEhlo(self):
40 for key in list(self.activeRelays.keys()):
41 if key != 'data' and key != 'scheme':
42 if 'protocolClient' in self.activeRelays[key]:
43 return self.activeRelays[key]['protocolClient'].session.ehlo_resp
45 def initConnection(self):
46 pass
48 def skipAuthentication(self):
49 self.socksSocket.send('220 Microsoft ESMTP MAIL Service ready'+EOL)
51 # Next should be the client sending the EHLO command
52 cmd, params = self.recvPacketClient().split(' ',1)
53 if cmd.upper() == 'EHLO':
54 clientcapabilities = self.getServerEhlo().split('\n')
55 # Don't offer these AUTH options so the client won't use them
56 # also don't offer STARTTLS since that will break things
57 blacklist = ['X-EXPS GSSAPI NTLM', 'STARTTLS', 'AUTH NTLM']
58 for cap in blacklist:
59 if cap in clientcapabilities:
60 clientcapabilities.remove(cap)
62 # Offer PLAIN auth for specifying the username
63 if 'AUTH PLAIN' not in clientcapabilities:
64 clientcapabilities.append('AUTH PLAIN')
65 # Offer LOGIN for specifying the username
66 if 'AUTH LOGIN' not in clientcapabilities:
67 clientcapabilities.append('AUTH LOGIN')
69 LOG.debug('SMTP: Sending mirrored capabilities from server: %s' % ', '.join(clientcapabilities))
70 # Prepare capabilities
71 delim = EOL+'250-'
72 caps = delim.join(clientcapabilities[:-1]) + EOL + '250 ' + clientcapabilities[-1] + EOL
73 self.socksSocket.send('250-%s' % caps)
74 else:
75 LOG.error('SMTP: Socks plugin expected EHLO command, but got: %s %s' % (cmd, params))
76 return False
77 # next
78 cmd, params = self.recvPacketClient().split(' ', 1)
79 args = params.split(' ')
80 if cmd.upper() == 'AUTH' and args[0] == 'LOGIN':
81 # OK, ask for their username
82 self.socksSocket.send('334 VXNlcm5hbWU6'+EOL)
83 # Client will now send their AUTH
84 data = self.socksSocket.recv(self.packetSize)
85 # This contains base64(username), decode
86 creds = base64.b64decode(data.strip())
87 self.username = creds.upper()
88 # Client will now send the password, we don't care for it but receive it anyway
89 self.socksSocket.send('334 UGFzc3dvcmQ6'+EOL)
90 data = self.socksSocket.recv(self.packetSize)
91 elif cmd.upper() == 'AUTH' and args[0] == 'PLAIN':
92 # Simple login
93 # This contains base64(\x00username\x00password), decode and split
94 creds = base64.b64decode(args[1].strip())
95 self.username = creds.split('\x00')[1].upper()
96 else:
97 LOG.error('SMTP: Socks plugin expected AUTH PLAIN or AUTH LOGIN command, but got: %s %s' % (cmd, params))
98 return False
100 # Check if we have a connection for the user
101 if self.username in self.activeRelays:
102 # Check the connection is not inUse
103 if self.activeRelays[self.username]['inUse'] is True:
104 LOG.error('SMTP: Connection for %s@%s(%s) is being used at the moment!' % (
105 self.username, self.targetHost, self.targetPort))
106 return False
107 else:
108 LOG.info('SMTP: Proxying client session for %s@%s(%s)' % (
109 self.username, self.targetHost, self.targetPort))
110 self.session = self.activeRelays[self.username]['protocolClient'].session
111 else:
112 LOG.error('SMTP: No session for %s@%s(%s) available' % (
113 self.username, self.targetHost, self.targetPort))
114 return False
116 # We arrived here, that means all is OK
117 self.socksSocket.send('235 2.7.0 Authentication successful%s' % EOL)
118 self.relaySocket = self.session.sock
119 self.relaySocketFile = self.session.file
120 return True
122 def tunnelConnection(self):
123 doneIndicator = EOL+'.'+EOL
124 while True:
125 data = self.socksSocket.recv(self.packetSize)
126 # If this returns with an empty string, it means the socket was closed
127 if data == '':
128 return
129 info = data.strip().split(' ')
130 # See if a QUIT command was sent, in which case we want to close
131 # the connection to the client but keep the relayed connection alive
132 if info[0].upper() == 'QUIT':
133 LOG.debug('Client sent QUIT command, closing socks connection to client')
134 self.socksSocket.send('221 2.0.0 Service closing transmission channel%s' % EOL)
135 return
136 self.relaySocket.send(data)
137 data = self.relaySocket.recv(self.packetSize)
138 self.socksSocket.send(data)
139 if info[0].upper() == 'DATA':
140 LOG.debug('SMTP Socks entering DATA transfer mode')
141 # DATA transfer, forward to the server till done
142 while data[-5:] != doneIndicator:
143 prevdata = data
144 data = self.socksSocket.recv(self.packetSize)
145 self.relaySocket.send(data)
146 if len(data) < 5:
147 # This can happen, the .CRLF will be in a packet after the first CRLF
148 # we stitch them back together for analysis
149 data = prevdata + data
150 LOG.debug('SMTP Socks DATA transfer mode finished')
151 # DATA done, forward server reply
152 data = self.relaySocket.recv(self.packetSize)
153 self.socksSocket.send(data)
155 def recvPacketClient(self):
156 data = self.socksSocket.recv(self.packetSize)
157 return data