From 73b669b49d1a86617495e504d2987ff276f28360 Mon Sep 17 00:00:00 2001 From: Joe Testa Date: Fri, 16 Feb 2024 21:58:51 -0500 Subject: [PATCH] Fixed parsing of ecdsa-sha2-nistp* CA signatures on host keys. Additionally, they are now flagged as potentially back-doored, just as standard host keys are. (#239) --- README.md | 1 + src/ssh_audit/hostkeytest.py | 6 +++++- src/ssh_audit/kexdh.py | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 971d91d2..1365fdd1 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,7 @@ For convenience, a web front-end on top of the command-line tool is available at ### v3.2.0-dev (???) - Expanded filter of CBC ciphers to flag for the Terrapin vulnerability. It now includes more rarely found ciphers. - Color output is disabled if the `NO_COLOR` environment variable is set (see https://no-color.org/). + - Fixed parsing of ecdsa-sha2-nistp* CA signatures on host keys. Additionally, they are now flagged as potentially back-doored, just as standard host keys are. ### v3.1.0 (2023-12-20) - Added test for the Terrapin message prefix truncation vulnerability ([CVE-2023-48795](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-48795)). diff --git a/src/ssh_audit/hostkeytest.py b/src/ssh_audit/hostkeytest.py index 3628b7a8..094b2b71 100644 --- a/src/ssh_audit/hostkeytest.py +++ b/src/ssh_audit/hostkeytest.py @@ -180,7 +180,7 @@ def perform_test(out: 'OutputBuffer', s: 'SSH_Socket', server_kex: 'SSH2_Kex', k hostkey_min_good = 256 hostkey_min_warn = 224 hostkey_warn_str = HostKeyTest.SMALL_ECC_MODULUS_WARNING - if ca_key_type.startswith('ssh-ed25519') or host_key_type.startswith('ecdsa-sha2-nistp'): + if ca_key_type.startswith('ssh-ed25519') or ca_key_type.startswith('ecdsa-sha2-nistp'): cakey_min_good = 256 cakey_min_warn = 224 cakey_warn_str = HostKeyTest.SMALL_ECC_MODULUS_WARNING @@ -209,6 +209,10 @@ def perform_test(out: 'OutputBuffer', s: 'SSH_Socket', server_kex: 'SSH2_Kex', k elif (0 < ca_modulus_size < cakey_min_good) and (cakey_warn_str not in key_warn_comments): key_warn_comments.append(cakey_warn_str) + # If the CA key type uses ECDSA with a NIST P-curve, fail it for possibly being back-doored. + if ca_key_type.startswith('ecdsa-sha2-nistp'): + key_fail_comments.append('CA key uses elliptic curves that are suspected as being backdoored by the U.S. National Security Agency') + # If this host key type is in the RSA family, then mark them all as parsed (since results in one are valid for them all). if host_key_type in HostKeyTest.RSA_FAMILY: for rsa_type in HostKeyTest.RSA_FAMILY: diff --git a/src/ssh_audit/kexdh.py b/src/ssh_audit/kexdh.py index 83cada23..85beb8ec 100644 --- a/src/ssh_audit/kexdh.py +++ b/src/ssh_audit/kexdh.py @@ -212,6 +212,15 @@ def __parse_ca_key(self, hostkey: bytes, hostkey_type: str, ptr: int) -> Tuple[s # CA's modulus. Bingo. ca_key_n, ca_key_n_len, ptr = KexDH.__get_bytes(ca_key, ptr) # pylint: disable=unused-variable + if ca_key_type.startswith("ecdsa-sha2-nistp") and ca_key_n_len > 0: + self.out.d("Found ecdsa-sha2-nistp* CA key type.") + + # 0x04 signifies that this is an uncompressed public key (meaning that full X and Y values are provided in ca_key_n. + if ca_key_n[0] == 4: + ca_key_n_len = ca_key_n_len - 1 # Subtract the 0x04 byte. + ca_key_n_len = int(ca_key_n_len / 2) # Divide by 2 since the modulus is the size of either the X or Y value. + + else: self.out.d("Certificate type %u found; this is not usually valid in the context of a host key! Skipping it..." % cert_type)