|  |  | @@ -25,11 +25,24 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  |    THE SOFTWARE. |  |  |  |    THE SOFTWARE. | 
			
		
	
		
		
			
				
					
					|  |  |  | """ |  |  |  | """ | 
			
		
	
		
		
			
				
					
					|  |  |  | from __future__ import print_function |  |  |  | from __future__ import print_function | 
			
		
	
		
		
			
				
					
					|  |  |  | import base64, binascii, errno, hashlib, getopt, io, os, random, re, select, socket, struct, sys, json |  |  |  | import base64 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import binascii | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import errno | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import getopt | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import hashlib | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import io | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import json | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import os | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import random | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import re | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import select | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import socket | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import struct | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import sys | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | VERSION = 'v2.2.1-dev' |  |  |  | VERSION = 'v2.2.1-dev' | 
			
		
	
		
		
			
				
					
					|  |  |  | SSH_HEADER = 'SSH-{0}-OpenSSH_8.0' # SSH software to impersonate |  |  |  | SSH_HEADER = 'SSH-{0}-OpenSSH_8.0'  # SSH software to impersonate | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | if sys.version_info.major < 3: |  |  |  | if sys.version_info.major < 3: | 
			
		
	
		
		
			
				
					
					|  |  |  |         print("\n!!!! NOTE: Python 2 is being considered for deprecation.  If you have a good reason to need continued Python 2 support, please e-mail jtesta@positronsecurity.com with your rationale.\n\n") |  |  |  |         print("\n!!!! NOTE: Python 2 is being considered for deprecation.  If you have a good reason to need continued Python 2 support, please e-mail jtesta@positronsecurity.com with your rationale.\n\n") | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -41,7 +54,7 @@ if sys.version_info >= (3,):  # pragma: nocover | 
			
		
	
		
		
			
				
					
					|  |  |  | else:  # pragma: nocover |  |  |  | else:  # pragma: nocover | 
			
		
	
		
		
			
				
					
					|  |  |  | 	import StringIO as _StringIO  # pylint: disable=import-error |  |  |  | 	import StringIO as _StringIO  # pylint: disable=import-error | 
			
		
	
		
		
			
				
					
					|  |  |  | 	StringIO = BytesIO = _StringIO.StringIO |  |  |  | 	StringIO = BytesIO = _StringIO.StringIO | 
			
		
	
		
		
			
				
					
					|  |  |  | 	text_type = unicode  # pylint: disable=undefined-variable |  |  |  | 	text_type = unicode  # pylint: disable=undefined-variable  # noqa: F821 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 	binary_type = str |  |  |  | 	binary_type = str | 
			
		
	
		
		
			
				
					
					|  |  |  | try:  # pragma: nocover |  |  |  | try:  # pragma: nocover | 
			
		
	
		
		
			
				
					
					|  |  |  | 	# pylint: disable=unused-import |  |  |  | 	# pylint: disable=unused-import | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -99,7 +112,7 @@ class AuditConf(object): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.ipv4 = False |  |  |  | 		self.ipv4 = False | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.ipv6 = False |  |  |  | 		self.ipv6 = False | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.timeout = 5.0 |  |  |  | 		self.timeout = 5.0 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.timeout_set = False # Set to True when the user explicitly sets it. |  |  |  | 		self.timeout_set = False  # Set to True when the user explicitly sets it. | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	def __setattr__(self, name, value): |  |  |  | 	def __setattr__(self, name, value): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		# type: (str, Union[str, int, bool, Sequence[int]]) -> None |  |  |  | 		# type: (str, Union[str, int, bool, Sequence[int]]) -> None | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -190,9 +203,9 @@ class AuditConf(object): | 
			
		
	
		
		
			
				
					
					|  |  |  | 			elif o in ('-t', '--timeout'): |  |  |  | 			elif o in ('-t', '--timeout'): | 
			
		
	
		
		
			
				
					
					|  |  |  | 				aconf.timeout = float(a) |  |  |  | 				aconf.timeout = float(a) | 
			
		
	
		
		
			
				
					
					|  |  |  | 				aconf.timeout_set = True |  |  |  | 				aconf.timeout_set = True | 
			
		
	
		
		
			
				
					
					|  |  |  | 		if len(args) == 0 and aconf.client_audit == False: |  |  |  | 		if len(args) == 0 and aconf.client_audit is False: | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			usage_cb() |  |  |  | 			usage_cb() | 
			
		
	
		
		
			
				
					
					|  |  |  | 		if aconf.client_audit == False: |  |  |  | 		if aconf.client_audit is False: | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			if oport is not None: |  |  |  | 			if oport is not None: | 
			
		
	
		
		
			
				
					
					|  |  |  | 				host = args[0] |  |  |  | 				host = args[0] | 
			
		
	
		
		
			
				
					
					|  |  |  | 			else: |  |  |  | 			else: | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -310,7 +323,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_OPENSSH74_UNSAFE = 'disabled (in client) since OpenSSH 7.4, unsafe algorithm' |  |  |  | 		WARN_OPENSSH74_UNSAFE = 'disabled (in client) since OpenSSH 7.4, unsafe algorithm' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_OPENSSH72_LEGACY = 'disabled (in client) since OpenSSH 7.2, legacy algorithm' |  |  |  | 		WARN_OPENSSH72_LEGACY = 'disabled (in client) since OpenSSH 7.2, legacy algorithm' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_OPENSSH70_LEGACY = 'removed since OpenSSH 7.0, legacy algorithm' |  |  |  | 		FAIL_OPENSSH70_LEGACY = 'removed since OpenSSH 7.0, legacy algorithm' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_OPENSSH70_WEAK   = 'removed (in server) and disabled (in client) since OpenSSH 7.0, weak algorithm' |  |  |  | 		FAIL_OPENSSH70_WEAK = 'removed (in server) and disabled (in client) since OpenSSH 7.0, weak algorithm' | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_OPENSSH70_LOGJAM = 'disabled (in client) since OpenSSH 7.0, logjam attack' |  |  |  | 		FAIL_OPENSSH70_LOGJAM = 'disabled (in client) since OpenSSH 7.0, logjam attack' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		INFO_OPENSSH69_CHACHA = 'default cipher since OpenSSH 6.9.' |  |  |  | 		INFO_OPENSSH69_CHACHA = 'default cipher since OpenSSH 6.9.' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_OPENSSH67_UNSAFE = 'removed (in server) since OpenSSH 6.7, unsafe algorithm' |  |  |  | 		FAIL_OPENSSH67_UNSAFE = 'removed (in server) since OpenSSH 6.7, unsafe algorithm' | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -319,21 +332,21 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_DBEAR67_DISABLED = 'disabled since Dropbear SSH 2015.67' |  |  |  | 		FAIL_DBEAR67_DISABLED = 'disabled since Dropbear SSH 2015.67' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_DBEAR53_DISABLED = 'disabled since Dropbear SSH 0.53' |  |  |  | 		FAIL_DBEAR53_DISABLED = 'disabled since Dropbear SSH 0.53' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_DEPRECATED_CIPHER = 'deprecated cipher' |  |  |  | 		FAIL_DEPRECATED_CIPHER = 'deprecated cipher' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_WEAK_CIPHER      = 'using weak cipher' |  |  |  | 		FAIL_WEAK_CIPHER = 'using weak cipher' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_WEAK_ALGORITHM   = 'using weak/obsolete algorithm' |  |  |  | 		FAIL_WEAK_ALGORITHM = 'using weak/obsolete algorithm' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_PLAINTEXT        = 'no encryption/integrity' |  |  |  | 		FAIL_PLAINTEXT = 'no encryption/integrity' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_DEPRECATED_MAC   = 'deprecated MAC' |  |  |  | 		FAIL_DEPRECATED_MAC = 'deprecated MAC' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_CURVES_WEAK      = 'using weak elliptic curves' |  |  |  | 		WARN_CURVES_WEAK = 'using weak elliptic curves' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_RNDSIG_KEY       = 'using weak random number generator could reveal the key' |  |  |  | 		WARN_RNDSIG_KEY = 'using weak random number generator could reveal the key' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_MODULUS_SIZE     = 'using small 1024-bit modulus' |  |  |  | 		WARN_MODULUS_SIZE = 'using small 1024-bit modulus' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_HASH_WEAK        = 'using weak hashing algorithm' |  |  |  | 		WARN_HASH_WEAK = 'using weak hashing algorithm' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_CIPHER_MODE      = 'using weak cipher mode' |  |  |  | 		WARN_CIPHER_MODE = 'using weak cipher mode' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_BLOCK_SIZE       = 'using small 64-bit block size' |  |  |  | 		WARN_BLOCK_SIZE = 'using small 64-bit block size' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_CIPHER_WEAK      = 'using weak cipher' |  |  |  | 		WARN_CIPHER_WEAK = 'using weak cipher' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_ENCRYPT_AND_MAC  = 'using encrypt-and-MAC mode' |  |  |  | 		WARN_ENCRYPT_AND_MAC = 'using encrypt-and-MAC mode' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_TAG_SIZE         = 'using small 64-bit tag size' |  |  |  | 		WARN_TAG_SIZE = 'using small 64-bit tag size' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_TAG_SIZE_96      = 'using small 96-bit tag size' |  |  |  | 		WARN_TAG_SIZE_96 = 'using small 96-bit tag size' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		WARN_EXPERIMENTAL     = 'using experimental algorithm' |  |  |  | 		WARN_EXPERIMENTAL = 'using experimental algorithm' | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		ALGORITHMS = { |  |  |  | 		ALGORITHMS = { | 
			
		
	
		
		
			
				
					
					|  |  |  |                         # Format: 'algorithm_name': [['version_first_appeared_in'], [reason_for_failure1, reason_for_failure2, ...], [warning1, warning2, ...]] |  |  |  |                         # Format: 'algorithm_name': [['version_first_appeared_in'], [reason_for_failure1, reason_for_failure2, ...], [warning1, warning2, ...]] | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -378,7 +391,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdh-sha2-nistp384': [['5.7,d2013.62'], [WARN_CURVES_WEAK]], |  |  |  | 				'ecdh-sha2-nistp384': [['5.7,d2013.62'], [WARN_CURVES_WEAK]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdh-sha2-nistp521': [['5.7,d2013.62'], [WARN_CURVES_WEAK]], |  |  |  | 				'ecdh-sha2-nistp521': [['5.7,d2013.62'], [WARN_CURVES_WEAK]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdh-sha2-nistt571': [[], [WARN_CURVES_WEAK]], |  |  |  | 				'ecdh-sha2-nistt571': [[], [WARN_CURVES_WEAK]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdh-sha2-1.3.132.0.10': [[]], # ECDH over secp256k1 (i.e.: the Bitcoin curve) |  |  |  | 				'ecdh-sha2-1.3.132.0.10': [[]],  # ECDH over secp256k1 (i.e.: the Bitcoin curve) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				'curve25519-sha256@libssh.org': [['6.5,d2013.62,l10.6.0']], |  |  |  | 				'curve25519-sha256@libssh.org': [['6.5,d2013.62,l10.6.0']], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'curve25519-sha256': [['7.4,d2018.76']], |  |  |  | 				'curve25519-sha256': [['7.4,d2018.76']], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'curve448-sha512': [[]], |  |  |  | 				'curve448-sha512': [[]], | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -386,8 +399,8 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'rsa1024-sha1': [[], [], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]], |  |  |  | 				'rsa1024-sha1': [[], [], [WARN_MODULUS_SIZE, WARN_HASH_WEAK]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'rsa2048-sha256': [[]], |  |  |  | 				'rsa2048-sha256': [[]], | 
			
		
	
		
		
			
				
					
					|  |  |  |                                 'sntrup4591761x25519-sha512@tinyssh.org': [['8.0'], [], [WARN_EXPERIMENTAL]], |  |  |  |                                 'sntrup4591761x25519-sha512@tinyssh.org': [['8.0'], [], [WARN_EXPERIMENTAL]], | 
			
		
	
		
		
			
				
					
					|  |  |  |                                 'ext-info-c': [[]], # Extension negotiation (RFC 8308) |  |  |  |                                 'ext-info-c': [[]],  # Extension negotiation (RFC 8308) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                 'ext-info-s': [[]], # Extension negotiation (RFC 8308) |  |  |  |                                 'ext-info-s': [[]],  # Extension negotiation (RFC 8308) | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			}, |  |  |  | 			}, | 
			
		
	
		
		
			
				
					
					|  |  |  | 			'key': { |  |  |  | 			'key': { | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ssh-rsa1': [[], [FAIL_WEAK_ALGORITHM]], |  |  |  | 				'ssh-rsa1': [[], [FAIL_WEAK_ALGORITHM]], | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -400,7 +413,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdsa-sha2-nistp256': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], |  |  |  | 				'ecdsa-sha2-nistp256': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdsa-sha2-nistp384': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], |  |  |  | 				'ecdsa-sha2-nistp384': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdsa-sha2-nistp521': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], |  |  |  | 				'ecdsa-sha2-nistp521': [['5.7,d2013.62,l10.6.4'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdsa-sha2-1.3.132.0.10': [[], [], [WARN_RNDSIG_KEY]], # ECDSA over secp256k1 (i.e.: the Bitcoin curve) |  |  |  | 				'ecdsa-sha2-1.3.132.0.10': [[], [], [WARN_RNDSIG_KEY]],  # ECDSA over secp256k1 (i.e.: the Bitcoin curve) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				'x509v3-sign-dss': [[], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]], |  |  |  | 				'x509v3-sign-dss': [[], [FAIL_OPENSSH70_WEAK], [WARN_MODULUS_SIZE, WARN_RNDSIG_KEY]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'x509v3-sign-rsa': [[], [], [WARN_HASH_WEAK]], |  |  |  | 				'x509v3-sign-rsa': [[], [], [WARN_HASH_WEAK]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'x509v3-sign-rsa-sha256@ssh.com': [[]], |  |  |  | 				'x509v3-sign-rsa-sha256@ssh.com': [[]], | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -416,7 +429,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'rsa-sha2-256-cert-v01@openssh.com': [['7.8']], |  |  |  | 				'rsa-sha2-256-cert-v01@openssh.com': [['7.8']], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'rsa-sha2-512-cert-v01@openssh.com': [['7.8']], |  |  |  | 				'rsa-sha2-512-cert-v01@openssh.com': [['7.8']], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ssh-rsa-sha256@ssh.com': [[]], |  |  |  | 				'ssh-rsa-sha256@ssh.com': [[]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdsa-sha2-1.3.132.0.10': [[], [], [WARN_RNDSIG_KEY]], # ECDSA over secp256k1 (i.e.: the Bitcoin curve) |  |  |  | 				'ecdsa-sha2-1.3.132.0.10': [[], [], [WARN_RNDSIG_KEY]],  # ECDSA over secp256k1 (i.e.: the Bitcoin curve) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				'sk-ecdsa-sha2-nistp256-cert-v01@openssh.com': [['8.2'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], |  |  |  | 				'sk-ecdsa-sha2-nistp256-cert-v01@openssh.com': [['8.2'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'sk-ecdsa-sha2-nistp256@openssh.com': [['8.2'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], |  |  |  | 				'sk-ecdsa-sha2-nistp256@openssh.com': [['8.2'], [WARN_CURVES_WEAK], [WARN_RNDSIG_KEY]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'sk-ssh-ed25519-cert-v01@openssh.com': [['8.2']], |  |  |  | 				'sk-ssh-ed25519-cert-v01@openssh.com': [['8.2']], | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -508,20 +521,20 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'umac-128@openssh.com': [['6.2'], [], [WARN_ENCRYPT_AND_MAC]], |  |  |  | 				'umac-128@openssh.com': [['6.2'], [], [WARN_ENCRYPT_AND_MAC]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'hmac-sha1-etm@openssh.com': [['6.2'], [], [WARN_HASH_WEAK]], |  |  |  | 				'hmac-sha1-etm@openssh.com': [['6.2'], [], [WARN_HASH_WEAK]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'hmac-sha1-96-etm@openssh.com': [['6.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_HASH_WEAK]], |  |  |  | 				'hmac-sha1-96-etm@openssh.com': [['6.2', '6.6', None], [FAIL_OPENSSH67_UNSAFE], [WARN_HASH_WEAK]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'hmac-sha2-256-96-etm@openssh.com': [[], [], [WARN_TAG_SIZE_96]], # Despite the @openssh.com tag, it doesn't appear that this was ever shipped with OpenSSH; it is only implemented in AsyncSSH (?). |  |  |  | 				'hmac-sha2-256-96-etm@openssh.com': [[], [], [WARN_TAG_SIZE_96]],  # Despite the @openssh.com tag, it doesn't appear that this was ever shipped with OpenSSH; it is only implemented in AsyncSSH (?). | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 				'hmac-sha2-512-96-etm@openssh.com': [[], [], [WARN_TAG_SIZE_96]], # Despite the @openssh.com tag, it doesn't appear that this was ever shipped with OpenSSH; it is only implemented in AsyncSSH (?). |  |  |  | 				'hmac-sha2-512-96-etm@openssh.com': [[], [], [WARN_TAG_SIZE_96]],  # Despite the @openssh.com tag, it doesn't appear that this was ever shipped with OpenSSH; it is only implemented in AsyncSSH (?). | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				'hmac-sha2-256-etm@openssh.com': [['6.2']], |  |  |  | 				'hmac-sha2-256-etm@openssh.com': [['6.2']], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'hmac-sha2-512-etm@openssh.com': [['6.2']], |  |  |  | 				'hmac-sha2-512-etm@openssh.com': [['6.2']], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'hmac-md5-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_HASH_WEAK]], |  |  |  | 				'hmac-md5-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_HASH_WEAK]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'hmac-md5-96-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_HASH_WEAK]], |  |  |  | 				'hmac-md5-96-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY, WARN_HASH_WEAK]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'hmac-ripemd160-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY]], |  |  |  | 				'hmac-ripemd160-etm@openssh.com': [['6.2', '6.6', '7.1'], [FAIL_OPENSSH67_UNSAFE], [WARN_OPENSSH72_LEGACY]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'umac-32@openssh.com': [[], [], [WARN_ENCRYPT_AND_MAC, WARN_TAG_SIZE]], # Despite having the @openssh.com suffix, this may never have shipped with OpenSSH (!). |  |  |  | 				'umac-32@openssh.com': [[], [], [WARN_ENCRYPT_AND_MAC, WARN_TAG_SIZE]],  # Despite having the @openssh.com suffix, this may never have shipped with OpenSSH (!). | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				'umac-64-etm@openssh.com': [['6.2'], [], [WARN_TAG_SIZE]], |  |  |  | 				'umac-64-etm@openssh.com': [['6.2'], [], [WARN_TAG_SIZE]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'umac-96@openssh.com': [[], [], [WARN_ENCRYPT_AND_MAC]], # Despite having the @openssh.com suffix, this may never have shipped with OpenSSH (!). |  |  |  | 				'umac-96@openssh.com': [[], [], [WARN_ENCRYPT_AND_MAC]],  # Despite having the @openssh.com suffix, this may never have shipped with OpenSSH (!). | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				'umac-128-etm@openssh.com': [['6.2']], |  |  |  | 				'umac-128-etm@openssh.com': [['6.2']], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'aes128-gcm': [[]], |  |  |  | 				'aes128-gcm': [[]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'aes256-gcm': [[]], |  |  |  | 				'aes256-gcm': [[]], | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'chacha20-poly1305@openssh.com': [[]], # Despite the @openssh.com tag, this was never shipped as a MAC in OpenSSH (only as a cipher); it is only implemented as a MAC in Syncplify. |  |  |  | 				'chacha20-poly1305@openssh.com': [[]],  # Despite the @openssh.com tag, this was never shipped as a MAC in OpenSSH (only as a cipher); it is only implemented as a MAC in Syncplify. | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			} |  |  |  | 			} | 
			
		
	
		
		
			
				
					
					|  |  |  | 		}  # type: Dict[str, Dict[str, List[List[Optional[str]]]]] |  |  |  | 		}  # type: Dict[str, Dict[str, List[List[Optional[str]]]]] | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -700,7 +713,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdh-sha2-nistp256': KexNISTP256, |  |  |  | 				'ecdh-sha2-nistp256': KexNISTP256, | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdh-sha2-nistp384': KexNISTP384, |  |  |  | 				'ecdh-sha2-nistp384': KexNISTP384, | 
			
		
	
		
		
			
				
					
					|  |  |  | 				'ecdh-sha2-nistp521': KexNISTP521, |  |  |  | 				'ecdh-sha2-nistp521': KexNISTP521, | 
			
		
	
		
		
			
				
					
					|  |  |  | 				#'kexguess2@matt.ucc.asn.au': ??? |  |  |  | 				# 'kexguess2@matt.ucc.asn.au': ??? | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			} |  |  |  | 			} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			# Pick the first kex algorithm that the server supports, which we |  |  |  | 			# Pick the first kex algorithm that the server supports, which we | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -735,14 +748,14 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 					# If the connection is closed, re-open it and get the kex again. |  |  |  | 					# If the connection is closed, re-open it and get the kex again. | 
			
		
	
		
		
			
				
					
					|  |  |  | 					if not s.is_connected(): |  |  |  | 					if not s.is_connected(): | 
			
		
	
		
		
			
				
					
					|  |  |  | 						s.connect() |  |  |  | 						s.connect() | 
			
		
	
		
		
			
				
					
					|  |  |  | 						unused = None # pylint: disable=unused-variable |  |  |  | 						unused = None  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 						unused, unused, err = s.get_banner() |  |  |  | 						unused, unused, err = s.get_banner() | 
			
		
	
		
		
			
				
					
					|  |  |  | 						if err is not None: |  |  |  | 						if err is not None: | 
			
		
	
		
		
			
				
					
					|  |  |  | 							s.close() |  |  |  | 							s.close() | 
			
		
	
		
		
			
				
					
					|  |  |  | 							return |  |  |  | 							return | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 						# Parse the server's initial KEX. |  |  |  | 						# Parse the server's initial KEX. | 
			
		
	
		
		
			
				
					
					|  |  |  | 						packet_type = 0 # pylint: disable=unused-variable |  |  |  | 						packet_type = 0  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 						packet_type, payload = s.read_packet() |  |  |  | 						packet_type, payload = s.read_packet() | 
			
		
	
		
		
			
				
					
					|  |  |  | 						SSH2.Kex.parse(payload) |  |  |  | 						SSH2.Kex.parse(payload) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -798,7 +811,6 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 					else: |  |  |  | 					else: | 
			
		
	
		
		
			
				
					
					|  |  |  | 						host_key_types[host_key_type]['parsed'] = True |  |  |  | 						host_key_types[host_key_type]['parsed'] = True | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	# Performs DH group exchanges to find what moduli are supported, and checks |  |  |  | 	# Performs DH group exchanges to find what moduli are supported, and checks | 
			
		
	
		
		
			
				
					
					|  |  |  | 	# their size. |  |  |  | 	# their size. | 
			
		
	
		
		
			
				
					
					|  |  |  | 	class GEXTest(object): |  |  |  | 	class GEXTest(object): | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -811,14 +823,14 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				return |  |  |  | 				return | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			s.connect() |  |  |  | 			s.connect() | 
			
		
	
		
		
			
				
					
					|  |  |  | 			unused = None # pylint: disable=unused-variable |  |  |  | 			unused = None  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			unused, unused, err = s.get_banner() |  |  |  | 			unused, unused, err = s.get_banner() | 
			
		
	
		
		
			
				
					
					|  |  |  | 			if err is not None: |  |  |  | 			if err is not None: | 
			
		
	
		
		
			
				
					
					|  |  |  | 				s.close() |  |  |  | 				s.close() | 
			
		
	
		
		
			
				
					
					|  |  |  | 				return False |  |  |  | 				return False | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			# Parse the server's initial KEX. |  |  |  | 			# Parse the server's initial KEX. | 
			
		
	
		
		
			
				
					
					|  |  |  | 			packet_type = 0 # pylint: disable=unused-variable |  |  |  | 			packet_type = 0  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			packet_type, payload = s.read_packet(2) |  |  |  | 			packet_type, payload = s.read_packet(2) | 
			
		
	
		
		
			
				
					
					|  |  |  | 			kex = SSH2.Kex.parse(payload) |  |  |  | 			kex = SSH2.Kex.parse(payload) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -865,7 +877,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 						# got here, doesn't mean the server is vulnerable... |  |  |  | 						# got here, doesn't mean the server is vulnerable... | 
			
		
	
		
		
			
				
					
					|  |  |  | 						smallest_modulus = kex_group.get_dh_modulus_size() |  |  |  | 						smallest_modulus = kex_group.get_dh_modulus_size() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 					except Exception as e: # pylint: disable=bare-except |  |  |  | 					except Exception:  # pylint: disable=bare-except | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 						pass |  |  |  | 						pass | 
			
		
	
		
		
			
				
					
					|  |  |  | 					finally: |  |  |  | 					finally: | 
			
		
	
		
		
			
				
					
					|  |  |  | 						s.close() |  |  |  | 						s.close() | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -887,9 +899,9 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 							kex_group.send_init_gex(s, bits, bits, bits) |  |  |  | 							kex_group.send_init_gex(s, bits, bits, bits) | 
			
		
	
		
		
			
				
					
					|  |  |  | 							kex_group.recv_reply(s, False) |  |  |  | 							kex_group.recv_reply(s, False) | 
			
		
	
		
		
			
				
					
					|  |  |  | 							smallest_modulus = kex_group.get_dh_modulus_size() |  |  |  | 							smallest_modulus = kex_group.get_dh_modulus_size() | 
			
		
	
		
		
			
				
					
					|  |  |  | 						except Exception as e: # pylint: disable=bare-except |  |  |  | 						except Exception:  # pylint: disable=bare-except | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 							#import traceback |  |  |  | 							# import traceback | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 							#print(traceback.format_exc()) |  |  |  | 							# print(traceback.format_exc()) | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 							pass |  |  |  | 							pass | 
			
		
	
		
		
			
				
					
					|  |  |  | 						finally: |  |  |  | 						finally: | 
			
		
	
		
		
			
				
					
					|  |  |  | 							# The server is in a state that is not re-testable, |  |  |  | 							# The server is in a state that is not re-testable, | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -897,7 +909,6 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 							# connection. |  |  |  | 							# connection. | 
			
		
	
		
		
			
				
					
					|  |  |  | 							s.close() |  |  |  | 							s.close() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 					if smallest_modulus > 0: |  |  |  | 					if smallest_modulus > 0: | 
			
		
	
		
		
			
				
					
					|  |  |  | 						kex.set_dh_modulus_size(gex_alg, smallest_modulus) |  |  |  | 						kex.set_dh_modulus_size(gex_alg, smallest_modulus) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -919,6 +930,7 @@ class SSH2(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 					if reconnect_failed: |  |  |  | 					if reconnect_failed: | 
			
		
	
		
		
			
				
					
					|  |  |  | 						break |  |  |  | 						break | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | class SSH1(object): |  |  |  | class SSH1(object): | 
			
		
	
		
		
			
				
					
					|  |  |  | 	class CRC32(object): |  |  |  | 	class CRC32(object): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		def __init__(self): |  |  |  | 		def __init__(self): | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -935,8 +947,8 @@ class SSH1(object): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		def calc(self, v): |  |  |  | 		def calc(self, v): | 
			
		
	
		
		
			
				
					
					|  |  |  | 			# type: (binary_type) -> int |  |  |  | 			# type: (binary_type) -> int | 
			
		
	
		
		
			
				
					
					|  |  |  | 			crc, l = 0, len(v) |  |  |  | 			crc, length = 0, len(v) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 			for i in range(l): |  |  |  | 			for i in range(length): | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				n = ord(v[i:i + 1]) |  |  |  | 				n = ord(v[i:i + 1]) | 
			
		
	
		
		
			
				
					
					|  |  |  | 				n = n ^ (crc & 0xff) |  |  |  | 				n = n ^ (crc & 0xff) | 
			
		
	
		
		
			
				
					
					|  |  |  | 				crc = (crc >> 8) ^ self._table[n] |  |  |  | 				crc = (crc >> 8) ^ self._table[n] | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -955,11 +967,11 @@ class SSH1(object): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	class KexDB(object):  # pylint: disable=too-few-public-methods |  |  |  | 	class KexDB(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 		# pylint: disable=bad-whitespace |  |  |  | 		# pylint: disable=bad-whitespace | 
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_PLAINTEXT        = 'no encryption/integrity' |  |  |  | 		FAIL_PLAINTEXT = 'no encryption/integrity' | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_OPENSSH37_REMOVE = 'removed since OpenSSH 3.7' |  |  |  | 		FAIL_OPENSSH37_REMOVE = 'removed since OpenSSH 3.7' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_NA_BROKEN        = 'not implemented in OpenSSH, broken algorithm' |  |  |  | 		FAIL_NA_BROKEN = 'not implemented in OpenSSH, broken algorithm' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		FAIL_NA_UNSAFE        = 'not implemented in OpenSSH (server), unsafe algorithm' |  |  |  | 		FAIL_NA_UNSAFE = 'not implemented in OpenSSH (server), unsafe algorithm' | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		TEXT_CIPHER_IDEA      = 'cipher used by commercial SSH' |  |  |  | 		TEXT_CIPHER_IDEA = 'cipher used by commercial SSH' | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		ALGORITHMS = { |  |  |  | 		ALGORITHMS = { | 
			
		
	
		
		
			
				
					
					|  |  |  | 			'key': { |  |  |  | 			'key': { | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -1189,6 +1201,7 @@ class ReadBuf(object): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self._len = 0 |  |  |  | 		self._len = 0 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		super(ReadBuf, self).reset() |  |  |  | 		super(ReadBuf, self).reset() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | class WriteBuf(object): |  |  |  | class WriteBuf(object): | 
			
		
	
		
		
			
				
					
					|  |  |  | 	def __init__(self, data=None): |  |  |  | 	def __init__(self, data=None): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		# type: (Optional[binary_type]) -> None |  |  |  | 		# type: (Optional[binary_type]) -> None | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -1285,15 +1298,15 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 	class Protocol(object):  # pylint: disable=too-few-public-methods |  |  |  | 	class Protocol(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 		# pylint: disable=bad-whitespace |  |  |  | 		# pylint: disable=bad-whitespace | 
			
		
	
		
		
			
				
					
					|  |  |  | 		SMSG_PUBLIC_KEY = 2 |  |  |  | 		SMSG_PUBLIC_KEY = 2 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		MSG_DEBUG       = 4 |  |  |  | 		MSG_DEBUG = 4 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		MSG_KEXINIT     = 20 |  |  |  | 		MSG_KEXINIT = 20 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		MSG_NEWKEYS     = 21 |  |  |  | 		MSG_NEWKEYS = 21 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		MSG_KEXDH_INIT  = 30 |  |  |  | 		MSG_KEXDH_INIT = 30 | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 		MSG_KEXDH_REPLY = 31 |  |  |  | 		MSG_KEXDH_REPLY = 31 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		MSG_KEXDH_GEX_REQUEST = 34 |  |  |  | 		MSG_KEXDH_GEX_REQUEST = 34 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		MSG_KEXDH_GEX_GROUP   = 31 |  |  |  | 		MSG_KEXDH_GEX_GROUP = 31 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		MSG_KEXDH_GEX_INIT    = 32 |  |  |  | 		MSG_KEXDH_GEX_INIT = 32 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		MSG_KEXDH_GEX_REPLY   = 33 |  |  |  | 		MSG_KEXDH_GEX_REPLY = 33 | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	class Product(object):  # pylint: disable=too-few-public-methods |  |  |  | 	class Product(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 		OpenSSH = 'OpenSSH' |  |  |  | 		OpenSSH = 'OpenSSH' | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -1609,7 +1622,7 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			def __getitem__(self, product): |  |  |  | 			def __getitem__(self, product): | 
			
		
	
		
		
			
				
					
					|  |  |  | 				# type: (str) -> Sequence[Optional[str]] |  |  |  | 				# type: (str) -> Sequence[Optional[str]] | 
			
		
	
		
		
			
				
					
					|  |  |  | 				return tuple(self.__storage.get(product, [None]*4)) |  |  |  | 				return tuple(self.__storage.get(product, [None] * 4)) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			def __str__(self): |  |  |  | 			def __str__(self): | 
			
		
	
		
		
			
				
					
					|  |  |  | 				# type: () -> str |  |  |  | 				# type: () -> str | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -1640,7 +1653,7 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 					ssh_versions[ssh_prod] = ssh_ver |  |  |  | 					ssh_versions[ssh_prod] = ssh_ver | 
			
		
	
		
		
			
				
					
					|  |  |  | 				for ssh_product, ssh_version in ssh_versions.items(): |  |  |  | 				for ssh_product, ssh_version in ssh_versions.items(): | 
			
		
	
		
		
			
				
					
					|  |  |  | 					if ssh_product not in self.__storage: |  |  |  | 					if ssh_product not in self.__storage: | 
			
		
	
		
		
			
				
					
					|  |  |  | 						self.__storage[ssh_product] = [None]*4 |  |  |  | 						self.__storage[ssh_product] = [None] * 4 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 					prev = self[ssh_product][pos] |  |  |  | 					prev = self[ssh_product][pos] | 
			
		
	
		
		
			
				
					
					|  |  |  | 					if (prev is None or |  |  |  | 					if (prev is None or | 
			
		
	
		
		
			
				
					
					|  |  |  | 					   (prev < ssh_version and pos % 2 == 0) or |  |  |  | 					   (prev < ssh_version and pos % 2 == 0) or | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -1783,19 +1796,18 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 			if software is not None: |  |  |  | 			if software is not None: | 
			
		
	
		
		
			
				
					
					|  |  |  | 				if software.product not in vproducts: |  |  |  | 				if software.product not in vproducts: | 
			
		
	
		
		
			
				
					
					|  |  |  | 					unknown_software = True |  |  |  | 					unknown_software = True | 
			
		
	
		
		
			
				
					
					|  |  |  | # |  |  |  |  | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | # The code below is commented out because it would try to guess what the server is, |  |  |  | 			# The code below is commented out because it would try to guess what the server is, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | # usually resulting in wild & incorrect recommendations. |  |  |  | 			# usually resulting in wild & incorrect recommendations. | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | # |  |  |  | 			# if software is None: | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #			if software is None: |  |  |  | 			# 	ssh_timeframe = self.get_ssh_timeframe(for_server) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #				ssh_timeframe = self.get_ssh_timeframe(for_server) |  |  |  | 			# 	for product in vproducts: | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #				for product in vproducts: |  |  |  | 			# 		if product not in ssh_timeframe: | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #					if product not in ssh_timeframe: |  |  |  | 			# 			continue | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #						continue |  |  |  | 			# 		version = ssh_timeframe.get_from(product, for_server) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #					version = ssh_timeframe.get_from(product, for_server) |  |  |  | 			# 		if version is not None: | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #					if version is not None: |  |  |  | 			# 			software = SSH.Software(None, product, version, None, None) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #						software = SSH.Software(None, product, version, None, None) |  |  |  | 			# 			break | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #						break |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			rec = {}  # type: Dict[int, Dict[str, Dict[str, Dict[str, int]]]] |  |  |  | 			rec = {}  # type: Dict[int, Dict[str, Dict[str, Dict[str, int]]]] | 
			
		
	
		
		
			
				
					
					|  |  |  | 			if software is None: |  |  |  | 			if software is None: | 
			
		
	
		
		
			
				
					
					|  |  |  | 				unknown_software = True |  |  |  | 				unknown_software = True | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2057,7 +2069,6 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 			self.client_host = None |  |  |  | 			self.client_host = None | 
			
		
	
		
		
			
				
					
					|  |  |  | 			self.client_port = None |  |  |  | 			self.client_port = None | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		def _resolve(self, ipvo): |  |  |  | 		def _resolve(self, ipvo): | 
			
		
	
		
		
			
				
					
					|  |  |  | 			# type: (Sequence[int]) -> Iterable[Tuple[int, Tuple[Any, ...]]] |  |  |  | 			# type: (Sequence[int]) -> Iterable[Tuple[int, Tuple[Any, ...]]] | 
			
		
	
		
		
			
				
					
					|  |  |  | 			ipvo = tuple([x for x in utils.unique_seq(ipvo) if x in (4, 6)]) |  |  |  | 			ipvo = tuple([x for x in utils.unique_seq(ipvo) if x in (4, 6)]) | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2081,7 +2092,6 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				out.fail('[exception] {0}'.format(e)) |  |  |  | 				out.fail('[exception] {0}'.format(e)) | 
			
		
	
		
		
			
				
					
					|  |  |  | 				sys.exit(1) |  |  |  | 				sys.exit(1) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		# Listens on a server socket and accepts one connection (used for |  |  |  | 		# Listens on a server socket and accepts one connection (used for | 
			
		
	
		
		
			
				
					
					|  |  |  | 		# auditing client connections). |  |  |  | 		# auditing client connections). | 
			
		
	
		
		
			
				
					
					|  |  |  | 		def listen_and_accept(self): |  |  |  | 		def listen_and_accept(self): | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2093,7 +2103,7 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				s.bind(('0.0.0.0', self.__port)) |  |  |  | 				s.bind(('0.0.0.0', self.__port)) | 
			
		
	
		
		
			
				
					
					|  |  |  | 				s.listen() |  |  |  | 				s.listen() | 
			
		
	
		
		
			
				
					
					|  |  |  | 				self.__sock_map[s.fileno()] = s |  |  |  | 				self.__sock_map[s.fileno()] = s | 
			
		
	
		
		
			
				
					
					|  |  |  | 			except Exception as e: |  |  |  | 			except Exception: | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				print("Warning: failed to listen on any IPv4 interfaces.") |  |  |  | 				print("Warning: failed to listen on any IPv4 interfaces.") | 
			
		
	
		
		
			
				
					
					|  |  |  | 				pass |  |  |  | 				pass | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2105,7 +2115,7 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 				s.bind(('::', self.__port)) |  |  |  | 				s.bind(('::', self.__port)) | 
			
		
	
		
		
			
				
					
					|  |  |  | 				s.listen() |  |  |  | 				s.listen() | 
			
		
	
		
		
			
				
					
					|  |  |  | 				self.__sock_map[s.fileno()] = s |  |  |  | 				self.__sock_map[s.fileno()] = s | 
			
		
	
		
		
			
				
					
					|  |  |  | 			except Exception as e: |  |  |  | 			except Exception: | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				print("Warning: failed to listen on any IPv6 interfaces.") |  |  |  | 				print("Warning: failed to listen on any IPv6 interfaces.") | 
			
		
	
		
		
			
				
					
					|  |  |  | 				pass |  |  |  | 				pass | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2139,7 +2149,6 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 			c.settimeout(self.__timeout) |  |  |  | 			c.settimeout(self.__timeout) | 
			
		
	
		
		
			
				
					
					|  |  |  | 			self.__sock = c |  |  |  | 			self.__sock = c | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		def connect(self): |  |  |  | 		def connect(self): | 
			
		
	
		
		
			
				
					
					|  |  |  | 			# type: () -> None |  |  |  | 			# type: () -> None | 
			
		
	
		
		
			
				
					
					|  |  |  | 			err = None |  |  |  | 			err = None | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2169,10 +2178,10 @@ class SSH(object):  # pylint: disable=too-few-public-methods | 
			
		
	
		
		
			
				
					
					|  |  |  | 			banner = SSH_HEADER.format('1.5' if sshv == 1 else '2.0') |  |  |  | 			banner = SSH_HEADER.format('1.5' if sshv == 1 else '2.0') | 
			
		
	
		
		
			
				
					
					|  |  |  | 			if self.__state < self.SM_BANNER_SENT: |  |  |  | 			if self.__state < self.SM_BANNER_SENT: | 
			
		
	
		
		
			
				
					
					|  |  |  | 				self.send_banner(banner) |  |  |  | 				self.send_banner(banner) | 
			
		
	
		
		
			
				
					
					|  |  |  | #			rto = self.__sock.gettimeout() |  |  |  | 			# rto = self.__sock.gettimeout() | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #			self.__sock.settimeout(0.7) |  |  |  | 			# self.__sock.settimeout(0.7) | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			s, e = self.recv() |  |  |  | 			s, e = self.recv() | 
			
		
	
		
		
			
				
					
					|  |  |  | #			self.__sock.settimeout(rto) |  |  |  | 			# self.__sock.settimeout(rto) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			if s < 0: |  |  |  | 			if s < 0: | 
			
		
	
		
		
			
				
					
					|  |  |  | 				return self.__banner, self.__header, e |  |  |  | 				return self.__banner, self.__header, e | 
			
		
	
		
		
			
				
					
					|  |  |  | 			e = None |  |  |  | 			e = None | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2352,12 +2361,11 @@ class KexDH(object):  # pragma: nocover | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__hostkey_type = None |  |  |  | 		self.__hostkey_type = None | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__hostkey_e = 0 |  |  |  | 		self.__hostkey_e = 0 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__hostkey_n = 0 |  |  |  | 		self.__hostkey_n = 0 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__hostkey_n_len = 0 # Length of the host key modulus. |  |  |  | 		self.__hostkey_n_len = 0  # Length of the host key modulus. | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__ca_n_len = 0 # Length of the CA key modulus (if hostkey is a cert). |  |  |  | 		self.__ca_n_len = 0  # Length of the CA key modulus (if hostkey is a cert). | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 		self.__f = 0 |  |  |  | 		self.__f = 0 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__h_sig = 0 |  |  |  | 		self.__h_sig = 0 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	def set_params(self, g, p): |  |  |  | 	def set_params(self, g, p): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__g = g |  |  |  | 		self.__g = g | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__p = p |  |  |  | 		self.__p = p | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2365,7 +2373,6 @@ class KexDH(object):  # pragma: nocover | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__x = 0 |  |  |  | 		self.__x = 0 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		self.__e = 0 |  |  |  | 		self.__e = 0 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	def send_init(self, s, init_msg=SSH.Protocol.MSG_KEXDH_INIT): |  |  |  | 	def send_init(self, s, init_msg=SSH.Protocol.MSG_KEXDH_INIT): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		# type: (SSH.Socket) -> None |  |  |  | 		# type: (SSH.Socket) -> None | 
			
		
	
		
		
			
				
					
					|  |  |  | 		r = random.SystemRandom() |  |  |  | 		r = random.SystemRandom() | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2395,17 +2402,16 @@ class KexDH(object):  # pragma: nocover | 
			
		
	
		
		
			
				
					
					|  |  |  | 			return None |  |  |  | 			return None | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		hostkey_len = f_len = h_sig_len = 0  # pylint: disable=unused-variable |  |  |  | 		hostkey_len = f_len = h_sig_len = 0  # pylint: disable=unused-variable | 
			
		
	
		
		
			
				
					
					|  |  |  | 		hostkey_type_len = hostkey_e_len = 0 # pylint: disable=unused-variable |  |  |  | 		hostkey_type_len = hostkey_e_len = 0  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		key_id_len = principles_len = 0      # pylint: disable=unused-variable |  |  |  | 		key_id_len = principles_len = 0  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		critical_options_len = extensions_len = 0        # pylint: disable=unused-variable |  |  |  | 		critical_options_len = extensions_len = 0  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		nonce_len = ca_key_len = ca_key_type_len = 0     # pylint: disable=unused-variable |  |  |  | 		nonce_len = ca_key_len = ca_key_type_len = 0  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 		ca_key_len = ca_key_type_len = ca_key_e_len = 0  # pylint: disable=unused-variable |  |  |  | 		ca_key_len = ca_key_type_len = ca_key_e_len = 0  # pylint: disable=unused-variable | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		key_id = principles = None           # pylint: disable=unused-variable |  |  |  | 		key_id = principles = None  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		critical_options = extensions = None # pylint: disable=unused-variable |  |  |  | 		critical_options = extensions = None  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		valid_after = valid_before = None    # pylint: disable=unused-variable |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 		nonce = ca_key = ca_key_type = None  # pylint: disable=unused-variable |  |  |  | 		nonce = ca_key = ca_key_type = None  # pylint: disable=unused-variable | 
			
		
	
		
		
			
				
					
					|  |  |  | 		ca_key_e = ca_key_n = None           # pylint: disable=unused-variable |  |  |  | 		ca_key_e = ca_key_n = None  # pylint: disable=unused-variable | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		# Get the host key blob, F, and signature. |  |  |  | 		# Get the host key blob, F, and signature. | 
			
		
	
		
		
			
				
					
					|  |  |  | 		ptr = 0 |  |  |  | 		ptr = 0 | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2455,12 +2461,10 @@ class KexDH(object):  # pragma: nocover | 
			
		
	
		
		
			
				
					
					|  |  |  | 				# The principles, which are... I don't know what. |  |  |  | 				# The principles, which are... I don't know what. | 
			
		
	
		
		
			
				
					
					|  |  |  | 				principles, principles_len, ptr = KexDH.__get_bytes(hostkey, ptr) |  |  |  | 				principles, principles_len, ptr = KexDH.__get_bytes(hostkey, ptr) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 				# The timestamp that this certificate is valid after. |  |  |  | 				# Skip over the timestamp that this certificate is valid after. | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 				valid_after = hostkey[ptr:ptr + 8] |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				ptr += 8 |  |  |  | 				ptr += 8 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 				# The timestamp that this certificate is valid before. |  |  |  | 				# Skip over the timestamp that this certificate is valid before. | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 				valid_before = hostkey[ptr:ptr + 8] |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 				ptr += 8 |  |  |  | 				ptr += 8 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 				# TODO: validate the principles, and time range. |  |  |  | 				# TODO: validate the principles, and time range. | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2895,7 +2899,7 @@ def output_fingerprints(algs, sha256=True): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		if algs.ssh1kex is not None: |  |  |  | 		if algs.ssh1kex is not None: | 
			
		
	
		
		
			
				
					
					|  |  |  | 			name = 'ssh-rsa1' |  |  |  | 			name = 'ssh-rsa1' | 
			
		
	
		
		
			
				
					
					|  |  |  | 			fp = SSH.Fingerprint(algs.ssh1kex.host_key_fingerprint_data) |  |  |  | 			fp = SSH.Fingerprint(algs.ssh1kex.host_key_fingerprint_data) | 
			
		
	
		
		
			
				
					
					|  |  |  | 			#bits = algs.ssh1kex.host_key_bits |  |  |  | 			# bits = algs.ssh1kex.host_key_bits | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			fps.append((name, fp)) |  |  |  | 			fps.append((name, fp)) | 
			
		
	
		
		
			
				
					
					|  |  |  | 		if algs.ssh2kex is not None: |  |  |  | 		if algs.ssh2kex is not None: | 
			
		
	
		
		
			
				
					
					|  |  |  | 			host_keys = algs.ssh2kex.host_keys() |  |  |  | 			host_keys = algs.ssh2kex.host_keys() | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2917,8 +2921,8 @@ def output_fingerprints(algs, sha256=True): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		for fpp in fps: |  |  |  | 		for fpp in fps: | 
			
		
	
		
		
			
				
					
					|  |  |  | 			name, fp = fpp |  |  |  | 			name, fp = fpp | 
			
		
	
		
		
			
				
					
					|  |  |  | 			fpo = fp.sha256 if sha256 else fp.md5 |  |  |  | 			fpo = fp.sha256 if sha256 else fp.md5 | 
			
		
	
		
		
			
				
					
					|  |  |  | 			#p = '' if out.batch else ' ' * (padlen - len(name)) |  |  |  | 			# p = '' if out.batch else ' ' * (padlen - len(name)) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 			#out.good('(fin) {0}{1} -- {2} {3}'.format(name, p, bits, fpo)) |  |  |  | 			# out.good('(fin) {0}{1} -- {2} {3}'.format(name, p, bits, fpo)) | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			out.good('(fin) {0}: {1}'.format(name, fpo)) |  |  |  | 			out.good('(fin) {0}: {1}'.format(name, fpo)) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	if len(obuf) > 0: |  |  |  | 	if len(obuf) > 0: | 
			
		
	
		
		
			
				
					
					|  |  |  | 		out.head('# fingerprints') |  |  |  | 		out.head('# fingerprints') | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2994,7 +2998,7 @@ def output_recommendations(algs, software, padlen=0): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		else: |  |  |  | 		else: | 
			
		
	
		
		
			
				
					
					|  |  |  | 			title = '' |  |  |  | 			title = '' | 
			
		
	
		
		
			
				
					
					|  |  |  | 		out.head('# algorithm recommendations {0}'.format(title)) |  |  |  | 		out.head('# algorithm recommendations {0}'.format(title)) | 
			
		
	
		
		
			
				
					
					|  |  |  | 		obuf.flush(True) # Sort the output so that it is always stable (needed for repeatable testing). |  |  |  | 		obuf.flush(True)  # Sort the output so that it is always stable (needed for repeatable testing). | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 		out.sep() |  |  |  | 		out.sep() | 
			
		
	
		
		
			
				
					
					|  |  |  | 	return ret |  |  |  | 	return ret | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -3018,7 +3022,7 @@ def output_info(algs, software, client_audit, any_problems, padlen=0): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def output(banner, header, client_host=None, kex=None, pkm=None): |  |  |  | def output(banner, header, client_host=None, kex=None, pkm=None): | 
			
		
	
		
		
			
				
					
					|  |  |  | 	# type: (Optional[SSH.Banner], List[text_type], Optional[SSH2.Kex], Optional[SSH1.PublicKeyMessage]) -> None |  |  |  | 	# type: (Optional[SSH.Banner], List[text_type], Optional[SSH2.Kex], Optional[SSH1.PublicKeyMessage]) -> None | 
			
		
	
		
		
			
				
					
					|  |  |  | 	client_audit = (client_host != None) # If set, this is a client audit. |  |  |  | 	client_audit = client_host is not None  # If set, this is a client audit. | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 	sshv = 1 if pkm is not None else 2 |  |  |  | 	sshv = 1 if pkm is not None else 2 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	algs = SSH.Algorithms(pkm, kex) |  |  |  | 	algs = SSH.Algorithms(pkm, kex) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	with OutputBuffer() as obuf: |  |  |  | 	with OutputBuffer() as obuf: | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -3077,11 +3081,11 @@ def output(banner, header, client_host=None, kex=None, pkm=None): | 
			
		
	
		
		
			
				
					
					|  |  |  | 	perfect_config = output_recommendations(algs, software, maxlen) |  |  |  | 	perfect_config = output_recommendations(algs, software, maxlen) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	output_info(algs, software, client_audit, not perfect_config) |  |  |  | 	output_info(algs, software, client_audit, not perfect_config) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	# If we encountered any unknown algorithms, ask the user to report them. |  |  |  | 	# If we encountered any unknown algorithms, ask the user to report them. | 
			
		
	
		
		
			
				
					
					|  |  |  | 	if len(unknown_algorithms) > 0: |  |  |  | 	if len(unknown_algorithms) > 0: | 
			
		
	
		
		
			
				
					
					|  |  |  | 		out.warn("\n\n!!! WARNING: unknown algorithm(s) found!: %s.  Please email the full output above to the maintainer (jtesta@positronsecurity.com), or create a Github issue at <https://github.com/jtesta/ssh-audit/issues>.\n" % ','.join(unknown_algorithms)) |  |  |  | 		out.warn("\n\n!!! WARNING: unknown algorithm(s) found!: %s.  Please email the full output above to the maintainer (jtesta@positronsecurity.com), or create a Github issue at <https://github.com/jtesta/ssh-audit/issues>.\n" % ','.join(unknown_algorithms)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | class Utils(object): |  |  |  | class Utils(object): | 
			
		
	
		
		
			
				
					
					|  |  |  | 	@classmethod |  |  |  | 	@classmethod | 
			
		
	
		
		
			
				
					
					|  |  |  | 	def _type_err(cls, v, target): |  |  |  | 	def _type_err(cls, v, target): | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -3204,6 +3208,7 @@ class Utils(object): | 
			
		
	
		
		
			
				
					
					|  |  |  | 		except:  # pylint: disable=bare-except |  |  |  | 		except:  # pylint: disable=bare-except | 
			
		
	
		
		
			
				
					
					|  |  |  | 			return -1.0 |  |  |  | 			return -1.0 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def build_struct(banner, kex=None, pkm=None, client_host=None): |  |  |  | def build_struct(banner, kex=None, pkm=None, client_host=None): | 
			
		
	
		
		
			
				
					
					|  |  |  | 	res = { |  |  |  | 	res = { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		"banner": { |  |  |  | 		"banner": { | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -3281,6 +3286,7 @@ def build_struct(banner, kex=None, pkm=None, client_host=None): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	return res |  |  |  | 	return res | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def audit(aconf, sshv=None): |  |  |  | def audit(aconf, sshv=None): | 
			
		
	
		
		
			
				
					
					|  |  |  | 	# type: (AuditConf, Optional[int]) -> None |  |  |  | 	# type: (AuditConf, Optional[int]) -> None | 
			
		
	
		
		
			
				
					
					|  |  |  | 	out.batch = aconf.batch |  |  |  | 	out.batch = aconf.batch | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -3350,9 +3356,11 @@ def audit(aconf, sshv=None): | 
			
		
	
		
		
			
				
					
					|  |  |  | utils = Utils() |  |  |  | utils = Utils() | 
			
		
	
		
		
			
				
					
					|  |  |  | out = Output() |  |  |  | out = Output() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def main(): |  |  |  | def main(): | 
			
		
	
		
		
			
				
					
					|  |  |  | 	conf = AuditConf.from_cmdline(sys.argv[1:], usage) |  |  |  | 	conf = AuditConf.from_cmdline(sys.argv[1:], usage) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	audit(conf) |  |  |  | 	audit(conf) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | if __name__ == '__main__':  # pragma: nocover |  |  |  | if __name__ == '__main__':  # pragma: nocover | 
			
		
	
		
		
			
				
					
					|  |  |  | 	main() |  |  |  | 	main() | 
			
		
	
	
		
		
			
				
					
					| 
						
						
						
						 |  |   |