mirror of
				https://github.com/jtesta/ssh-audit.git
				synced 2025-10-31 05:25:28 +01:00 
			
		
		
		
	Added support for kex GSS wildcards (#143).
This commit is contained in:
		| @@ -71,29 +71,13 @@ class SSH2_KexDB:  # pylint: disable=too-few-public-methods | |||||||
|     ALGORITHMS: Dict[str, Dict[str, List[List[Optional[str]]]]] = { |     ALGORITHMS: Dict[str, Dict[str, List[List[Optional[str]]]]] = { | ||||||
|         # Format: 'algorithm_name': [['version_first_appeared_in'], [reason_for_failure1, reason_for_failure2, ...], [warning1, warning2, ...], [info1, info2, ...]] |         # Format: 'algorithm_name': [['version_first_appeared_in'], [reason_for_failure1, reason_for_failure2, ...], [warning1, warning2, ...], [info1, info2, ...]] | ||||||
|         'kex': { |         'kex': { | ||||||
|             'diffie-hellman-group1-sha1': [['2.3.0,d0.28,l10.2', '6.6', '6.9'], [FAIL_1024BIT_MODULUS, FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_HASH_WEAK]], |             # The GSS kex algorithms get special wildcard handling, since they include variable base64 data after their standard prefixes. | ||||||
|             'gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==': [[], [FAIL_1024BIT_MODULUS, FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_HASH_WEAK]], |  | ||||||
|             'gss-gex-sha1-eipGX3TCiQSrx573bT1o1Q==': [[], [], [WARN_HASH_WEAK]], |  | ||||||
|             'gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==': [[], [], [WARN_HASH_WEAK]], |  | ||||||
|             'gss-gex-sha1-': [[], [], [WARN_HASH_WEAK]], |  | ||||||
|             'gss-group1-sha1-eipGX3TCiQSrx573bT1o1Q==': [[], [FAIL_1024BIT_MODULUS], [WARN_HASH_WEAK]], |  | ||||||
|             'gss-group1-sha1-': [[], [FAIL_1024BIT_MODULUS], [WARN_HASH_WEAK]], |  | ||||||
|             'gss-group14-sha1-': [[], [], [WARN_HASH_WEAK, WARN_2048BIT_MODULUS]], |  | ||||||
|             'gss-group14-sha1-eipGX3TCiQSrx573bT1o1Q==': [[], [], [WARN_HASH_WEAK, WARN_2048BIT_MODULUS]], |  | ||||||
|             'gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==': [[], [], [WARN_HASH_WEAK, WARN_2048BIT_MODULUS]], |  | ||||||
|             'gss-group14-sha256-': [[], [], [WARN_2048BIT_MODULUS]], |  | ||||||
|             'gss-group14-sha256-toWM5Slw5Ew8Mqkay+al2g==': [[], [], [WARN_2048BIT_MODULUS]], |  | ||||||
|             'gss-group15-sha512-': [[]], |  | ||||||
|             'gss-group15-sha512-toWM5Slw5Ew8Mqkay+al2g==': [[]], |  | ||||||
|             'gss-group16-sha512-': [[]], |  | ||||||
|             'gss-nistp256-sha256-': [[], [FAIL_CURVES_WEAK]], |  | ||||||
|             'gss-curve25519-sha256-': [[]], |  | ||||||
|             'gss-13.3.132.0.10-sha256-*': [[], [FAIL_UNKNOWN]], |             'gss-13.3.132.0.10-sha256-*': [[], [FAIL_UNKNOWN]], | ||||||
|             'gss-curve25519-sha256-*': [[]], |             'gss-curve25519-sha256-*': [[]], | ||||||
|             'gss-curve448-sha512-*': [[]], |             'gss-curve448-sha512-*': [[]], | ||||||
|             'gss-gex-sha1-*': [[], [], [WARN_HASH_WEAK]], |             'gss-gex-sha1-*': [[], [], [WARN_HASH_WEAK]], | ||||||
|             'gss-gex-sha256-*': [[]], |             'gss-gex-sha256-*': [[]], | ||||||
|             'gss-group1-sha1-*': [[], [FAIL_1024BIT_MODULUS], [WARN_HASH_WEAK]], |             'gss-group1-sha1-*': [[], [FAIL_1024BIT_MODULUS, FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_HASH_WEAK]], | ||||||
|             'gss-group14-sha1-*': [[], [], [WARN_HASH_WEAK, WARN_2048BIT_MODULUS]], |             'gss-group14-sha1-*': [[], [], [WARN_HASH_WEAK, WARN_2048BIT_MODULUS]], | ||||||
|             'gss-group14-sha256-*': [[], [], [WARN_2048BIT_MODULUS]], |             'gss-group14-sha256-*': [[], [], [WARN_2048BIT_MODULUS]], | ||||||
|             'gss-group15-sha512-*': [[]], |             'gss-group15-sha512-*': [[]], | ||||||
| @@ -103,6 +87,7 @@ class SSH2_KexDB:  # pylint: disable=too-few-public-methods | |||||||
|             'gss-nistp256-sha256-*': [[], [FAIL_CURVES_WEAK]], |             'gss-nistp256-sha256-*': [[], [FAIL_CURVES_WEAK]], | ||||||
|             'gss-nistp384-sha256-*': [[], [FAIL_CURVES_WEAK]], |             'gss-nistp384-sha256-*': [[], [FAIL_CURVES_WEAK]], | ||||||
|             'gss-nistp521-sha512-*': [[], [FAIL_CURVES_WEAK]], |             'gss-nistp521-sha512-*': [[], [FAIL_CURVES_WEAK]], | ||||||
|  |             'diffie-hellman-group1-sha1': [['2.3.0,d0.28,l10.2', '6.6', '6.9'], [FAIL_1024BIT_MODULUS, FAIL_OPENSSH67_UNSAFE, FAIL_OPENSSH70_LOGJAM], [WARN_HASH_WEAK]], | ||||||
|             'diffie-hellman-group1-sha256': [[], [FAIL_1024BIT_MODULUS]], |             'diffie-hellman-group1-sha256': [[], [FAIL_1024BIT_MODULUS]], | ||||||
|             'diffie-hellman-group14-sha1': [['3.9,d0.53,l10.6.0'], [], [WARN_HASH_WEAK, WARN_2048BIT_MODULUS]], |             'diffie-hellman-group14-sha1': [['3.9,d0.53,l10.6.0'], [], [WARN_HASH_WEAK, WARN_2048BIT_MODULUS]], | ||||||
|             'diffie-hellman-group14-sha256': [['7.3,d2016.73'], [], [WARN_2048BIT_MODULUS]], |             'diffie-hellman-group14-sha256': [['7.3,d2016.73'], [], [WARN_2048BIT_MODULUS]], | ||||||
|   | |||||||
| @@ -137,6 +137,12 @@ def output_algorithm(out: OutputBuffer, alg_db: Dict[str, Dict[str, List[List[Op | |||||||
|             alg_name_with_size = '%s (%d-bit)' % (alg_name, hostkey_size) |             alg_name_with_size = '%s (%d-bit)' % (alg_name, hostkey_size) | ||||||
|             padding = padding[0:-11] |             padding = padding[0:-11] | ||||||
|  |  | ||||||
|  |     # If this is a kex algorithm and starts with 'gss-', then normalize its name (i.e.: 'gss-gex-sha1-vz8J1E9PzLr8b1K+0remTg==' => 'gss-gex-sha1-*').  The base64 field can vary, so we'll convert it to the wildcard that our database uses and we'll just resume doing a straight match like all other algorithm names. | ||||||
|  |     alg_name_original = alg_name | ||||||
|  |     if alg_type == 'kex' and alg_name.startswith('gss-'): | ||||||
|  |         last_dash = alg_name.rindex('-') | ||||||
|  |         alg_name = "%s-*" % alg_name[0:last_dash] | ||||||
|  |  | ||||||
|     texts = [] |     texts = [] | ||||||
|     if len(alg_name.strip()) == 0: |     if len(alg_name.strip()) == 0: | ||||||
|         return program_retval |         return program_retval | ||||||
| @@ -162,6 +168,10 @@ def output_algorithm(out: OutputBuffer, alg_db: Dict[str, Dict[str, List[List[Op | |||||||
|         texts.append(('warn', 'unknown algorithm')) |         texts.append(('warn', 'unknown algorithm')) | ||||||
|         unknown_algs.append(alg_name) |         unknown_algs.append(alg_name) | ||||||
|  |  | ||||||
|  |     # For kex GSS algorithms, now that we already did the database lookup (above), restore the original algorithm name so its reported properly in the output. | ||||||
|  |     if alg_name != alg_name_original: | ||||||
|  |         alg_name = alg_name_original | ||||||
|  |  | ||||||
|     alg_name = alg_name_with_size if alg_name_with_size is not None else alg_name |     alg_name = alg_name_with_size if alg_name_with_size is not None else alg_name | ||||||
|     first = True |     first = True | ||||||
|     for level, text in texts: |     for level, text in texts: | ||||||
|   | |||||||
| @@ -61,6 +61,23 @@ class TestSSH2: | |||||||
|         w.write_int(0) |         w.write_int(0) | ||||||
|         return w.write_flush() |         return w.write_flush() | ||||||
|  |  | ||||||
|  |     def _kex_payload_with_gss(self): | ||||||
|  |         w = self.wbuf() | ||||||
|  |         w.write(b'\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff') | ||||||
|  |         w.write_list(['gss-gex-sha1-dZuIebMjgUqaxvbF7hDbAw==', 'gss-gex-sha1-vz8J1E9PzLr8b1K+0remTg==', 'gss-group14-sha1-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group14-sha1-vz8J1E9PzLr8b1K+0remTg==', 'gss-group14-sha256-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group14-sha256-vz8J1E9PzLr8b1K+0remTg==', 'gss-group16-sha512-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group16-sha512-vz8J1E9PzLr8b1K+0remTg==', 'gss-group18-sha512-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group18-sha512-vz8J1E9PzLr8b1K+0remTg==', 'gss-group1-sha1-dZuIebMjgUqaxvbF7hDbAw==', 'gss-group1-sha1-vz8J1E9PzLr8b1K+0remTg==', 'gss-curve448-sha512-XXX']) | ||||||
|  |         w.write_list(['ssh-ed25519']) | ||||||
|  |         w.write_list(['chacha20-poly1305@openssh.com']) | ||||||
|  |         w.write_list(['chacha20-poly1305@openssh.com']) | ||||||
|  |         w.write_list(['hmac-sha2-512-etm@openssh.com']) | ||||||
|  |         w.write_list(['hmac-sha2-512-etm@openssh.com']) | ||||||
|  |         w.write_list(['none', 'zlib@openssh.com']) | ||||||
|  |         w.write_list(['none', 'zlib@openssh.com']) | ||||||
|  |         w.write_list(['']) | ||||||
|  |         w.write_list(['']) | ||||||
|  |         w.write_byte(False) | ||||||
|  |         w.write_int(0) | ||||||
|  |         return w.write_flush() | ||||||
|  |  | ||||||
|     def test_kex_read(self): |     def test_kex_read(self): | ||||||
|         kex = self.ssh2_kex.parse(self._kex_payload()) |         kex = self.ssh2_kex.parse(self._kex_payload()) | ||||||
|         assert kex is not None |         assert kex is not None | ||||||
| @@ -163,3 +180,22 @@ class TestSSH2: | |||||||
|         lines = output_spy.flush() |         lines = output_spy.flush() | ||||||
|         assert len(lines) == 9 |         assert len(lines) == 9 | ||||||
|         assert 'unknown message' in lines[-1] |         assert 'unknown message' in lines[-1] | ||||||
|  |  | ||||||
|  |     def test_ssh2_gss_kex(self, output_spy, virtual_socket): | ||||||
|  |         '''Ensure that GSS kex algorithms are properly parsed.''' | ||||||
|  |  | ||||||
|  |         vsocket = virtual_socket | ||||||
|  |         w = self.wbuf() | ||||||
|  |         w.write_byte(self.protocol.MSG_KEXINIT) | ||||||
|  |         w.write(self._kex_payload_with_gss())  # Use the kex with GSS algorithms. | ||||||
|  |         vsocket.rdata.append(b'SSH-2.0-OpenSSH_7.3 ssh-audit-test\r\n') | ||||||
|  |         vsocket.rdata.append(self._create_ssh2_packet(w.write_flush())) | ||||||
|  |         output_spy.begin() | ||||||
|  |         out = self.OutputBuffer() | ||||||
|  |         self.audit(out, self._conf()) | ||||||
|  |         out.write() | ||||||
|  |         lines = output_spy.flush() | ||||||
|  |  | ||||||
|  |         # Ensure that none of the lines are reported as "unknown algorithm". | ||||||
|  |         for line in lines: | ||||||
|  |             assert line.find('unknown algorithm') == -1 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Joe Testa
					Joe Testa