mirror of
				https://github.com/jtesta/ssh-audit.git
				synced 2025-10-28 16:17:30 +01:00 
			
		
		
		
	Now handles exceptions during server KEX parsing more gracefully.
This commit is contained in:
		| @@ -177,6 +177,7 @@ For convenience, a web front-end on top of the command-line tool is available at | |||||||
| ## ChangeLog | ## ChangeLog | ||||||
| ### v2.5.0-dev (???) | ### v2.5.0-dev (???) | ||||||
|  - Fixed crash when running host key tests. |  - Fixed crash when running host key tests. | ||||||
|  |  - Handles server connection failures more gracefully. | ||||||
|  - Now prints JSON with indents when `-jj` is used (useful for debugging). |  - Now prints JSON with indents when `-jj` is used (useful for debugging). | ||||||
|  - Added MD5 fingerprints to verbose output. |  - Added MD5 fingerprints to verbose output. | ||||||
|  - Added `-d`/`--debug` option for getting debugging output; credit [Adam Russell](https://github.com/thecliguy). |  - Added `-d`/`--debug` option for getting debugging output; credit [Adam Russell](https://github.com/thecliguy). | ||||||
|   | |||||||
| @@ -26,6 +26,8 @@ | |||||||
| from typing import Dict, List, Set, Sequence, Tuple, Iterable  # noqa: F401 | from typing import Dict, List, Set, Sequence, Tuple, Iterable  # noqa: F401 | ||||||
| from typing import Callable, Optional, Union, Any  # noqa: F401 | from typing import Callable, Optional, Union, Any  # noqa: F401 | ||||||
|  |  | ||||||
|  | import traceback | ||||||
|  |  | ||||||
| from ssh_audit.kexdh import KexGroupExchange_SHA1, KexGroupExchange_SHA256 | from ssh_audit.kexdh import KexGroupExchange_SHA1, KexGroupExchange_SHA256 | ||||||
| from ssh_audit.ssh2_kexdb import SSH2_KexDB | from ssh_audit.ssh2_kexdb import SSH2_KexDB | ||||||
| from ssh_audit.ssh2_kex import SSH2_Kex | from ssh_audit.ssh2_kex import SSH2_Kex | ||||||
| @@ -58,9 +60,13 @@ class GEXTest: | |||||||
|         # server's own values. |         # server's own values. | ||||||
|         s.send_kexinit(key_exchanges=[gex_alg], hostkeys=kex.key_algorithms, ciphers=kex.server.encryption, macs=kex.server.mac, compressions=kex.server.compression, languages=kex.server.languages) |         s.send_kexinit(key_exchanges=[gex_alg], hostkeys=kex.key_algorithms, ciphers=kex.server.encryption, macs=kex.server.mac, compressions=kex.server.compression, languages=kex.server.languages) | ||||||
|  |  | ||||||
|         # Parse the server's KEX. |         try: | ||||||
|         _, payload = s.read_packet(2) |             # Parse the server's KEX. | ||||||
|         SSH2_Kex.parse(payload) |             _, payload = s.read_packet(2) | ||||||
|  |             SSH2_Kex.parse(payload) | ||||||
|  |         except Exception: | ||||||
|  |             out.v("Failed to parse server's kex.  Stack trace:\n%s" % str(traceback.format_exc()), write_now=True) | ||||||
|  |             return False | ||||||
|  |  | ||||||
|         return True |         return True | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,6 +26,8 @@ | |||||||
| from typing import Dict, List, Set, Sequence, Tuple, Iterable  # noqa: F401 | from typing import Dict, List, Set, Sequence, Tuple, Iterable  # noqa: F401 | ||||||
| from typing import Callable, Optional, Union, Any  # noqa: F401 | from typing import Callable, Optional, Union, Any  # noqa: F401 | ||||||
|  |  | ||||||
|  | import traceback | ||||||
|  |  | ||||||
| from ssh_audit.kexdh import KexDH, KexGroup1, KexGroup14_SHA1, KexGroup14_SHA256, KexCurve25519_SHA256, KexGroup16_SHA512, KexGroup18_SHA512, KexGroupExchange_SHA1, KexGroupExchange_SHA256, KexNISTP256, KexNISTP384, KexNISTP521 | from ssh_audit.kexdh import KexDH, KexGroup1, KexGroup14_SHA1, KexGroup14_SHA256, KexCurve25519_SHA256, KexGroup16_SHA512, KexGroup18_SHA512, KexGroupExchange_SHA1, KexGroupExchange_SHA256, KexNISTP256, KexNISTP384, KexNISTP521 | ||||||
| from ssh_audit.ssh2_kex import SSH2_Kex | from ssh_audit.ssh2_kex import SSH2_Kex | ||||||
| from ssh_audit.ssh2_kexdb import SSH2_KexDB | from ssh_audit.ssh2_kexdb import SSH2_KexDB | ||||||
| @@ -123,10 +125,13 @@ class HostKeyTest: | |||||||
|                     # Send our KEX using the specified group-exchange and most of the server's own values. |                     # Send our KEX using the specified group-exchange and most of the server's own values. | ||||||
|                     s.send_kexinit(key_exchanges=[kex_str], hostkeys=[host_key_type], ciphers=server_kex.server.encryption, macs=server_kex.server.mac, compressions=server_kex.server.compression, languages=server_kex.server.languages) |                     s.send_kexinit(key_exchanges=[kex_str], hostkeys=[host_key_type], ciphers=server_kex.server.encryption, macs=server_kex.server.mac, compressions=server_kex.server.compression, languages=server_kex.server.languages) | ||||||
|  |  | ||||||
|                     # Parse the server's KEX. |                     try: | ||||||
|                     _, payload = s.read_packet() |                         # Parse the server's KEX. | ||||||
|                     SSH2_Kex.parse(payload) |                         _, payload = s.read_packet() | ||||||
|  |                         SSH2_Kex.parse(payload) | ||||||
|  |                     except Exception: | ||||||
|  |                         out.v("Failed to parse server's kex.  Stack trace:\n%s" % str(traceback.format_exc()), write_now=True) | ||||||
|  |                         return | ||||||
|  |  | ||||||
|                 # Do the initial DH exchange.  The server responds back |                 # Do the initial DH exchange.  The server responds back | ||||||
|                 # with the host key and its length.  Bingo.  We also get back the host key fingerprint. |                 # with the host key and its length.  Bingo.  We also get back the host key fingerprint. | ||||||
|   | |||||||
| @@ -895,7 +895,12 @@ def audit(out: OutputBuffer, aconf: AuditConf, sshv: Optional[int] = None, print | |||||||
|     if sshv == 1: |     if sshv == 1: | ||||||
|         program_retval = output(out, aconf, banner, header, pkm=SSH1_PublicKeyMessage.parse(payload)) |         program_retval = output(out, aconf, banner, header, pkm=SSH1_PublicKeyMessage.parse(payload)) | ||||||
|     elif sshv == 2: |     elif sshv == 2: | ||||||
|         kex = SSH2_Kex.parse(payload) |         try: | ||||||
|  |             kex = SSH2_Kex.parse(payload) | ||||||
|  |         except Exception: | ||||||
|  |             out.fail("Failed to parse server's kex.  Stack trace:\n%s" % str(traceback.format_exc())) | ||||||
|  |             return exitcodes.CONNECTION_ERROR | ||||||
|  |  | ||||||
|         if aconf.client_audit is False: |         if aconf.client_audit is False: | ||||||
|             HostKeyTest.run(out, s, kex) |             HostKeyTest.run(out, s, kex) | ||||||
|             GEXTest.run(out, s, kex) |             GEXTest.run(out, s, kex) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Joe Testa
					Joe Testa