Skip to content

Commit

Permalink
Add support for STARTTLS over XMPP. See --starttls and --xmpp_to.
Browse files Browse the repository at this point in the history
Update issue 8
Add support for STARTTLS over XMPP. See --starttls and --xmpp_to.
  • Loading branch information
nabla-c0d3 committed Feb 26, 2012
1 parent 316523a commit c7606ea
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 20 deletions.
51 changes: 41 additions & 10 deletions discover_targets.py
Expand Up @@ -97,9 +97,15 @@ def discover_targets(args_target_list, args_command_list, available_commands, ma
if args_command_list.starttls == 'smtp':
default_port = 25
test_stattls = test_starttls_smtp
test_starttls_args = ()
elif args_command_list.starttls == 'xmpp':
default_port = 5222
test_stattls = test_starttls_xmpp
test_starttls_args = args_command_list.xmpp_to
else:
default_port = 443
test_stattls = None
test_starttls_args = ()

for target in args_target_list:
try:
Expand All @@ -116,7 +122,8 @@ def discover_targets(args_target_list, args_command_list, available_commands, ma
(host,port),
socket_timeout,
result_queue,
test_stattls))
test_stattls,
test_starttls_args))
worker.start()
thread_list.append(worker)

Expand Down Expand Up @@ -147,7 +154,7 @@ def discover_targets(args_target_list, args_command_list, available_commands, ma
return alive_target_list


def _test_connect(target, timeout, out_q=None, test_starttls=None):
def _test_connect(target, timeout, out_q=None, test_starttls=None, test_starttls_args=()):
"""
Try to connect to the given target=(host,port) and put the result in out_q.
"""
Expand All @@ -161,17 +168,15 @@ def _test_connect(target, timeout, out_q=None, test_starttls=None):
s.connect((host, port))
# Host is up => keep the IP address we actually connected to
ip_addr = s.getpeername()
if test_starttls:
error_text = test_starttls(s, host, test_starttls_args)

except socket.timeout: # Host is down
error_text = 'WARNING: Could not connect (timeout), discarding corresponding tasks.'
except socket.gaierror: # Host is down
error_text = 'WARNING: Could not connect, discarding corresponding tasks.'
except socket.error: # Connection Refused
error_text = 'WARNING: Connection rejected, discarding corresponding tasks.'

else:
if test_starttls:
error_text = test_starttls(s)

finally:
s.close()
Expand All @@ -181,22 +186,48 @@ def _test_connect(target, timeout, out_q=None, test_starttls=None):
return ip_addr


def test_starttls_smtp(s):
def test_starttls_smtp(s, host):
"""
Using a socket already connected to an SMTP server, try to initiate a
STARTLS handshake.
"""
error_text = ''
# Send a EHLO and wait for the 250 status
smtp_resp = s.recv(2048)
s.recv(2048)
s.send('EHLO sslyze.scan\r\n')
smtp_resp = s.recv(2048)
if '250 ' not in s.recv(2048):
return 'WARNING: SMTP EHLO was rejected, discarding corresponding tasks.'

# Semd a STARTTLS
s.send('STARTTLS\r\n')
smtp_resp = s.recv(2048)
if 'Ready to start TLS' not in smtp_resp:
error_text = 'WARNING: STARTTLS not supported, discarding corresponding tasks.'
error_text = 'WARNING: SMTP STARTTLS not supported, discarding corresponding tasks.'

return error_text


def test_starttls_xmpp(sock, host, xmpp_to):
"""
Using a socket already connected to an XMPP server, try to initiate a
STARTLS handshake.
"""
xmpp_open_stream = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' xmlns:tls='http://www.ietf.org/rfc/rfc2595.txt' to='{0}'>"
xmpp_starttls = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"

if xmpp_to is None:
xmpp_to = host

error_text = ''
# Open an XMPP stream
sock.send(xmpp_open_stream.format(xmpp_to))
if '<stream:error>' in sock.recv(2048):
return 'WARNING: Error opening XMPP stream, discarding corresponding tasks. Consider using --xmpp_to ?'

# Send a STARTTLS
sock.send(xmpp_starttls)
if 'proceed' not in sock.recv(2048):
error_text = 'WARNING: XMPP STARTTLS not supported, discarding corresponding tasks.'

return error_text

Expand Down
18 changes: 14 additions & 4 deletions parse_command_line.py
Expand Up @@ -93,11 +93,20 @@ def create_command_line_parser(available_plugins, prog_version, timeout):
parser.add_option(
'--starttls',
help= (
'Uses STARTTLS to scan an SMTP server.'
' STARTTLS should be \'smtp\'. '),
'Uses STARTTLS to scan a server.'
' STARTTLS should be \'smtp\' or \'xmpp\'.'),
dest='starttls',
default=None)

parser.add_option(
'--xmpp_to',
help= (
'Optional setting for STARTTLS XMPP. '
' XMPP_TO should be the hostname to be put in the \'to\' attribute '
'of the XMPP stream. Default is the server\'s hostname.'),
dest='xmpp_to',
default=None)

# Add plugin options to the parser
for plugin_class in available_plugins:
pluginoptiongroup = plugin_class.get_commands()
Expand Down Expand Up @@ -215,11 +224,12 @@ def process_parsing_results(args_command_list):
shared_settings['https_tunnel_port'] = None

# STARTTLS
if args_command_list.starttls not in [None,'smtp']:
print PARSING_ERROR_FORMAT.format('--starttls should be \'smtp\'.')
if args_command_list.starttls not in [None,'smtp','xmpp']:
print PARSING_ERROR_FORMAT.format('--starttls should be \'smtp\' or \'xmpp\'.')
return
else:
shared_settings['starttls'] = args_command_list.starttls
shared_settings['xmpp_to'] = args_command_list.xmpp_to

if args_command_list.starttls and args_command_list.https_tunnel:
print PARSING_ERROR_FORMAT.format(
Expand Down
73 changes: 70 additions & 3 deletions utils/STARTTLS.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#-------------------------------------------------------------------------------
# Name: STARTTLS.py
# Purpose: ctSSL-based STARTTLS support for SMTP.
# Purpose: Quick and dirty ctSSL-based STARTTLS support for SMTP and XMPP.
#
# Author: alban
#
Expand Down Expand Up @@ -63,15 +63,15 @@ def connect(self):


# Get the SMTP banner
smtp_resp = sock.recv(2048)
sock.recv(2048)

# Send a EHLO and wait for the 250 status
sock.send('EHLO sslyze.scan\r\n')
smtp_resp = sock.recv(2048)
if '250 ' not in smtp_resp:
raise SSLHandshakeError('SMTP EHLO was rejected ?')

# Semd a STARTTLS
# Send a STARTTLS
sock.send('STARTTLS\r\n')
smtp_resp = sock.recv(2048)
if 'Ready to start TLS' not in smtp_resp:
Expand All @@ -92,4 +92,71 @@ def connect(self):

def close(self):
self.sock.close()



class XMPPConnection():

default_port = 5222
xmpp_open_stream = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' xmlns:tls='http://www.ietf.org/rfc/rfc2595.txt' to='{0}'>"
xmpp_starttls = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"

def __init__(self, host, port=default_port, ssl=None, ssl_ctx=None,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT, xmpp_to=None):

self.ssl_ctx = ssl_ctx
self.ssl = ssl
self.host = host
self.port = port
self.timeout = timeout
self.sock = None
if xmpp_to is None:
self.xmpp_to = host
else:
self.xmpp_to = xmpp_to

if self.ssl_ctx is None:
self.ssl_ctx = SSL_CTX.SSL_CTX()
# Can't verify certs by default
self.ssl_ctx.set_verify(constants.SSL_VERIFY_NONE)

if self.ssl is None:
self.ssl = SSL.SSL(self.ssl_ctx)


def connect(self):
"""
Connect to a host on a given (SSL) port, send a STARTTLS command,
and perform the SSL handshake.
"""

sock = socket.create_connection((self.host, self.port),
self.timeout)
self.sock = sock

# Open an XMPP stream
sock.send(self.xmpp_open_stream.format(self.xmpp_to))
sock.recv(2048)

# Send a STARTTLS
sock.send(self.xmpp_starttls)
xmpp_resp = sock.recv(2048)
if 'proceed' not in xmpp_resp:
raise SSLHandshakeError('XMPP STARTTLS not supported ?')

# Do the SSL handshake
self.ssl.set_socket(sock)
ssl_sock = SSLSocket(self.ssl)


try:
ssl_sock.do_handshake()
except Exception as e:
filter_handshake_exceptions(e)

self.sock = ssl_sock


def close(self):
self.sock.close()

17 changes: 14 additions & 3 deletions utils/SharedSettingsHelper.py
Expand Up @@ -36,10 +36,19 @@ def create_ssl_connection(target, shared_settings, ssl=None, ssl_ctx=None):
timeout = shared_settings['timeout']
(host, ip_addr, port) = target

if shared_settings['starttls']:
if shared_settings['starttls'] == 'smtp':
ssl_connection = STARTTLS.SMTPConnection(ip_addr, port, ssl, ssl_ctx,
timeout=timeout)

elif shared_settings['starttls'] == 'xmpp':
if shared_settings['xmpp_to']:
xmpp_to = shared_settings['xmpp_to']
else:
xmpp_to = host

ssl_connection = \
STARTTLS.XMPPConnection(ip_addr, port, ssl, ssl_ctx,
timeout=timeout, xmpp_to=xmpp_to)

elif shared_settings['https_tunnel_host']:
# Using an HTTP CONNECT proxy to tunnel SSL traffic
tunnel_host = shared_settings['https_tunnel_host']
Expand Down Expand Up @@ -86,12 +95,14 @@ def check_ssl_connection_is_alive(ssl_connection, shared_settings):
"""

result = 'N/A'
if shared_settings['starttls']:
if shared_settings['starttls'] == 'smtp':
try:
ssl_connection.sock.send('NOOP\r\n')
result = ssl_connection.sock.read(2048).strip()
except socket.timeout:
result = 'Timeout on SMTP NOOP'
elif shared_settings['starttls'] == 'xmpp':
result = 'OK'
else:
try:
# Send an HTTP GET to the server and store the HTTP Status Code
Expand Down

0 comments on commit c7606ea

Please sign in to comment.