diff --git a/docs/content/en/integrations/parsers/file/ssh_audit.md b/docs/content/en/integrations/parsers/file/ssh_audit.md new file mode 100644 index 00000000000..e5877f79380 --- /dev/null +++ b/docs/content/en/integrations/parsers/file/ssh_audit.md @@ -0,0 +1,5 @@ +--- +title: "SSH Audit" +toc_hide: true +--- +Import JSON output of ssh_audit report. See \ No newline at end of file diff --git a/dojo/tools/ssh_audit/__init__.py b/dojo/tools/ssh_audit/__init__.py new file mode 100644 index 00000000000..99e8e118c6a --- /dev/null +++ b/dojo/tools/ssh_audit/__init__.py @@ -0,0 +1 @@ +__author__ = "manuel_sommer" diff --git a/dojo/tools/ssh_audit/parser.py b/dojo/tools/ssh_audit/parser.py new file mode 100644 index 00000000000..ec8745ea7c6 --- /dev/null +++ b/dojo/tools/ssh_audit/parser.py @@ -0,0 +1,204 @@ +import json +from dojo.models import Endpoint, Finding + + +class SSHAuditParser(object): + def get_scan_types(self): + return ["SSH Audit Importer"] + + def get_label_for_scan_types(self, scan_type): + return scan_type # no custom label for now + + def get_description_for_scan_types(self, scan_type): + return "Import result of SSH Audit JSON output." + + def convert_cvss_score(self, raw_value): + """According to CVSS official numbers https://nvd.nist.gov/vuln-metrics/cvss + None 0.0 + Low 0.0-3.9 Low 0.1-3.9 + Medium 4.0-6.9 Medium 4.0-6.9 + High 7.0-10.0 High 7.0-8.9 + Critical 9.0-10.0""" + val = float(raw_value) + if val == 0.0: + return "Info" + elif val < 4.0: + return "Low" + elif val < 7.0: + return "Medium" + elif val < 9.0: + return "High" + else: + return "Critical" + + def get_findings(self, filename, test): + items = [] + try: + data = json.load(filename) + except ValueError as err: + data = {} + if data != {}: + title = data['banner']['raw'] + for cve in data['cves']: + cvename = cve['name'] + description = [f"**CVE**: {cvename}"] + description.append(f"**Description**: {cve['description']}") + description.append(f"**Banner**: {title}") + severity = self.convert_cvss_score(raw_value=cve['cvssv2']) + finding = Finding(title=str(title) + "_" + str(cvename), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + for kex in data['kex']: + if 'fail' in kex['notes'] and 'warn' in kex['notes']: + kexname = kex['algorithm'] + description = [f"**Algorithm**: {kexname}"] + description.append(f"**Description Failure**: {kex['notes']['fail']}") + description.append(f"**Description Warning**: {kex['notes']['warn']}") + description.append(f"**Info**: {kex['notes']['info']}") + severity = "High" + finding = Finding(title=str(title) + "_" + str(kexname), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + elif 'fail' in kex['notes']: + kexname = kex['algorithm'] + description = [f"**Algorithm**: {kexname}"] + description.append(f"**Description Failure**: {kex['notes']['fail']}") + description.append(f"**Info**: {kex['notes']['info']}") + severity = "High" + finding = Finding(title=str(title) + "_" + str(kexname), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + elif 'warn' in kex['notes']: + kexname = kex['algorithm'] + description = [f"**Algorithm**: {kexname}"] + description.append(f"**Description Warning**: {kex['notes']['warn']}") + description.append(f"**Info**: {kex['notes']['info']}") + severity = "Medium" + finding = Finding(title=str(title) + "_" + str(kexname), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + for key in data['key']: + if 'fail' in key['notes'] and 'warn' in key['notes']: + keyname = key['algorithm'] + description = [f"**Algorithm**: {keyname}"] + description.append(f"**Description Failure**: {key['notes']['fail']}") + description.append(f"**Description Warning**: {key['notes']['warn']}") + if 'keysize' in key: + description.append(f"**KeySize**: {key['keysize']}") + description.append(f"**Info**: {key['notes']['info']}") + severity = "High" + finding = Finding(title=str(title) + "_" + str(keyname), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + elif 'fail' in key['notes']: + keyname = key['algorithm'] + description = [f"**Algorithm**: {keyname}"] + description.append(f"**Description Failure**: {key['notes']['fail']}") + if 'keysize' in key: + description.append(f"**KeySize**: {key['keysize']}") + description.append(f"**Info**: {key['notes']['info']}") + severity = "High" + finding = Finding(title=str(title) + "_" + str(keyname), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + elif 'warn' in key['notes']: + keyname = key['algorithm'] + description = [f"**Algorithm**: {keyname}"] + description.append(f"**Description Warning**: {key['notes']['warn']}") + if 'keysize' in key: + description.append(f"**KeySize**: {key['keysize']}") + description.append(f"**Info**: {key['notes']['info']}") + severity = "Medium" + finding = Finding(title=str(title) + "_" + str(keyname), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + for mac in data['mac']: + if 'fail' in mac['notes'] and 'warn' in mac['notes']: + macname = mac['algorithm'] + description = [f"**Algorithm**: {macname}"] + description.append(f"**Description Failure**: {mac['notes']['fail']}") + description.append(f"**Description Warning**: {mac['notes']['warn']}") + description.append(f"**Info**: {mac['notes']['info']}") + severity = "High" + finding = Finding(title=str(title) + "_" + str(macname), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + elif 'fail' in mac['notes']: + macname = mac['algorithm'] + description = [f"**Algorithm**: {macname}"] + description.append(f"**Description Failure**: {mac['notes']['fail']}") + description.append(f"**Info**: {mac['notes']['info']}") + severity = "High" + finding = Finding(title=str(title) + "_" + str(macname), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + elif 'warn' in mac['notes']: + macname = mac['algorithm'] + description = [f"**Algorithm**: {macname}"] + description.append(f"**Description Warning**: {mac['notes']['warn']}") + description.append(f"**Info**: {mac['notes']['info']}") + severity = "Medium" + finding = Finding(title=str(title) + "_" + str(macname), + test=test, + description="\n".join(description), + severity=severity, + static_finding=False) + items.append(finding) + finding.unsaved_endpoints = list() + endpoint = Endpoint(host=data['target'].split(':')[0], port=data['target'].split(':')[1]) + finding.unsaved_endpoints.append(endpoint) + return items diff --git a/unittests/scans/ssh_audit/many_vulns.json b/unittests/scans/ssh_audit/many_vulns.json new file mode 100644 index 00000000000..44d15ee91af --- /dev/null +++ b/unittests/scans/ssh_audit/many_vulns.json @@ -0,0 +1,469 @@ +{ + "banner":{ + "comments":"Debian-10+deb10u2", + "protocol":"2.0", + "raw":"SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2", + "software":"OpenSSH_7.9p1" + }, + "compression":[ + "none", + "zlib@openssh.com" + ], + "cves":[ + { + "cvssv2":7.0, + "description":"privilege escalation via supplemental groups", + "name":"CVE-2021-41617" + }, + { + "cvssv2":7.8, + "description":"command injection via anomalous argument transfers", + "name":"CVE-2020-15778" + }, + { + "cvssv2":7.8, + "description":"memory corruption and local code execution via pre-authentication integer overflow", + "name":"CVE-2019-16905" + }, + { + "cvssv2":5.3, + "description":"enumerate usernames via challenge response", + "name":"CVE-2016-20012" + } + ], + "enc":[ + { + "algorithm":"chacha20-poly1305@openssh.com", + "notes":{ + "info":[ + "default cipher since OpenSSH 6.9", + "available since OpenSSH 6.5" + ] + } + }, + { + "algorithm":"aes128-ctr", + "notes":{ + "info":[ + "available since OpenSSH 3.7, Dropbear SSH 0.52" + ] + } + }, + { + "algorithm":"aes192-ctr", + "notes":{ + "info":[ + "available since OpenSSH 3.7" + ] + } + }, + { + "algorithm":"aes256-ctr", + "notes":{ + "info":[ + "available since OpenSSH 3.7, Dropbear SSH 0.52" + ] + } + }, + { + "algorithm":"aes128-gcm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"aes256-gcm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + } + ], + "fingerprints":[ + { + "hash":"mHoRf3V/hprQTMrO1PcDB2FSGhB61MlDJ//eWMtkkjE", + "hash_alg":"SHA256", + "hostkey":"ssh-ed25519" + }, + { + "hash":"df:8c:70:0c:d4:18:76:81:83:9e:39:05:6d:f1:01:75", + "hash_alg":"MD5", + "hostkey":"ssh-ed25519" + }, + { + "hash":"W1xWUfJ7EU3CEi4etW6JwLbQZz04gtYEfc8YGIouNyc", + "hash_alg":"SHA256", + "hostkey":"ssh-rsa" + }, + { + "hash":"f4:a2:aa:82:f8:fe:b1:06:de:9a:da:dc:bc:5d:e3:6c", + "hash_alg":"MD5", + "hostkey":"ssh-rsa" + } + ], + "kex":[ + { + "algorithm":"curve25519-sha256", + "notes":{ + "info":[ + "default fail key exchange since OpenSSH 6.4", + "available since OpenSSH 7.4, Dropbear SSH 2018.76" + ] + } + }, + { + "algorithm":"curve25519-sha256@libssh.org", + "notes":{ + "info":[ + "default key exchange since OpenSSH 6.4", + "available since OpenSSH 6.4, Dropbear SSH 2013.62" + ] + } + }, + { + "algorithm":"ecdh-sha2-nistp256", + "notes":{ + "fail":[ + "using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency" + ], + "info":[ + "available since OpenSSH 5.7, Dropbear SSH 2013.62" + ] + } + }, + { + "algorithm":"ecdh-sha2-nistp384", + "notes":{ + "fail":[ + "using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency" + ], + "info":[ + "available since OpenSSH 5.7, Dropbear SSH 2013.62" + ] + } + }, + { + "algorithm":"ecdh-sha2-nistp521", + "notes":{ + "fail":[ + "using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency" + ], + "info":[ + "available since OpenSSH 5.7, Dropbear SSH 2013.62" + ] + } + }, + { + "algorithm":"diffie-hellman-group-exchange-sha256", + "notes":{ + "info":[ + "available since OpenSSH 4.4" + ] + } + }, + { + "algorithm":"diffie-hellman-group16-sha512", + "notes":{ + "info":[ + "available since OpenSSH 7.3, Dropbear SSH 2016.73" + ] + } + }, + { + "algorithm":"diffie-hellman-group18-sha512", + "notes":{ + "info":[ + "available since OpenSSH 7.3" + ] + } + }, + { + "algorithm":"diffie-hellman-group14-sha256", + "notes":{ + "info":[ + "available since OpenSSH 7.3, Dropbear SSH 2016.73" + ], + "warn":[ + "2048-bit modulus only provides 112-bits of symmetric strength" + ] + } + }, + { + "algorithm":"diffie-hellman-group14-sha1", + "notes":{ + "fail":[ + "using broken SHA-1 hash algorithm" + ], + "info":[ + "available since OpenSSH 3.9, Dropbear SSH 0.53" + ], + "warn":[ + "2048-bit modulus only provides 112-bits of symmetric strength" + ] + } + } + ], + "key":[ + { + "algorithm":"rsa-sha2-512", + "keysize":2048, + "notes":{ + "info":[ + "available since OpenSSH 7.2" + ], + "warn":[ + "2048-bit modulus only provides 112-bits of symmetric strength" + ] + } + }, + { + "algorithm":"rsa-sha2-256", + "keysize":2048, + "notes":{ + "info":[ + "available since OpenSSH 7.2" + ], + "warn":[ + "2048-bit modulus only provides 112-bits of symmetric strength" + ] + } + }, + { + "algorithm":"ssh-rsa", + "keysize":2048, + "notes":{ + "fail":[ + "using broken SHA-1 hash algorithm" + ], + "info":[ + "deprecated in OpenSSH 8.8: https://www.openssh.com/txt/release-8.8", + "available since OpenSSH 2.5.0, Dropbear SSH 0.28" + ], + "warn":[ + "2048-bit modulus only provides 112-bits of symmetric strength" + ] + } + }, + { + "algorithm":"ecdsa-sha2-nistp256", + "notes":{ + "fail":[ + "using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency" + ], + "info":[ + "available since OpenSSH 5.7, Dropbear SSH 2013.62" + ], + "warn":[ + "using weak random number generator could reveal the key" + ] + } + }, + { + "algorithm":"ssh-ed25519", + "notes":{ + "info":[ + "available since OpenSSH 6.5" + ] + } + } + ], + "mac":[ + { + "algorithm":"umac-64-etm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ], + "warn":[ + "using small 64-bit tag size" + ] + } + }, + { + "algorithm":"umac-128-etm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"hmac-sha2-256-etm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"hmac-sha2-512-etm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"hmac-sha1-etm@openssh.com", + "notes":{ + "fail":[ + "using broken SHA-1 hash algorithm" + ], + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"umac-64@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 4.7" + ], + "warn":[ + "using encrypt-and-MAC mode", + "using small 64-bit tag size" + ] + } + }, + { + "algorithm":"umac-128@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ], + "warn":[ + "using encrypt-and-MAC mode" + ] + } + }, + { + "algorithm":"hmac-sha2-256", + "notes":{ + "info":[ + "available since OpenSSH 5.9, Dropbear SSH 2013.56" + ], + "warn":[ + "using encrypt-and-MAC mode" + ] + } + }, + { + "algorithm":"hmac-sha2-512", + "notes":{ + "info":[ + "available since OpenSSH 5.9, Dropbear SSH 2013.56" + ], + "warn":[ + "using encrypt-and-MAC mode" + ] + } + }, + { + "algorithm":"hmac-sha1", + "notes":{ + "fail":[ + "using broken SHA-1 hash algorithm" + ], + "info":[ + "available since OpenSSH 2.1.0, Dropbear SSH 0.28" + ], + "warn":[ + "using encrypt-and-MAC mode" + ] + } + } + ], + "recommendations":{ + "critical":{ + "del":{ + "kex":[ + { + "name":"diffie-hellman-group14-sha1", + "notes":"" + }, + { + "name":"ecdh-sha2-nistp256", + "notes":"" + }, + { + "name":"ecdh-sha2-nistp384", + "notes":"" + }, + { + "name":"ecdh-sha2-nistp521", + "notes":"" + } + ], + "key":[ + { + "name":"ecdsa-sha2-nistp256", + "notes":"" + }, + { + "name":"ssh-rsa", + "notes":"" + } + ], + "mac":[ + { + "name":"hmac-sha1", + "notes":"" + }, + { + "name":"hmac-sha1-etm@openssh.com", + "notes":"" + } + ] + } + }, + "warning":{ + "chg":{ + "key":[ + { + "name":"rsa-sha2-256", + "notes":"increase modulus size to 3072 bits or larger" + }, + { + "name":"rsa-sha2-512", + "notes":"increase modulus size to 3072 bits or larger" + } + ] + }, + "del":{ + "kex":[ + { + "name":"diffie-hellman-group14-sha256", + "notes":"" + } + ], + "mac":[ + { + "name":"hmac-sha2-256", + "notes":"" + }, + { + "name":"hmac-sha2-512", + "notes":"" + }, + { + "name":"umac-128@openssh.com", + "notes":"" + }, + { + "name":"umac-64-etm@openssh.com", + "notes":"" + }, + { + "name":"umac-64@openssh.com", + "notes":"" + } + ] + } + } + }, + "target":"sdf.sdf.fewio:22" + } \ No newline at end of file diff --git a/unittests/scans/ssh_audit/many_vulns2.json b/unittests/scans/ssh_audit/many_vulns2.json new file mode 100644 index 00000000000..c516ff80e1c --- /dev/null +++ b/unittests/scans/ssh_audit/many_vulns2.json @@ -0,0 +1,404 @@ +{ + "banner":{ + "comments":"Ubuntu-3ubuntu0.4", + "protocol":"2.0", + "raw":"SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.4", + "software":"OpenSSH_8.9p1" + }, + "compression":[ + "none", + "zlib@openssh.com" + ], + "cves":[ + + ], + "enc":[ + { + "algorithm":"chacha20-poly1305@openssh.com", + "notes":{ + "info":[ + "default cipher since OpenSSH 6.9", + "available since OpenSSH 6.5" + ] + } + }, + { + "algorithm":"aes128-ctr", + "notes":{ + "info":[ + "available since OpenSSH 3.7, Dropbear SSH 0.52" + ] + } + }, + { + "algorithm":"aes192-ctr", + "notes":{ + "info":[ + "available since OpenSSH 3.7" + ] + } + }, + { + "algorithm":"aes256-ctr", + "notes":{ + "info":[ + "available since OpenSSH 3.7, Dropbear SSH 0.52" + ] + } + }, + { + "algorithm":"aes128-gcm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"aes256-gcm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + } + ], + "fingerprints":[ + { + "hash":"7HGjPCpM7KL+xEDT+o4oMsuLphK9emAFY4T9fglsCjE", + "hash_alg":"SHA256", + "hostkey":"ssh-ed25519" + }, + { + "hash":"f5:30:24:c3:30:91:30:31:02:d6:44:9d:66:2e:92:8e", + "hash_alg":"MD5", + "hostkey":"ssh-ed25519" + }, + { + "hash":"v9O1CYNZpN+Ng3R+49vHmiBoJ6WhvMQ1Z4BeHcWFE4E", + "hash_alg":"SHA256", + "hostkey":"ssh-rsa" + }, + { + "hash":"0f:3b:05:af:12:cb:89:a0:41:01:47:55:b5:74:be:96", + "hash_alg":"MD5", + "hostkey":"ssh-rsa" + } + ], + "kex":[ + { + "algorithm":"curve25519-sha256", + "notes":{ + "info":[ + "default key exchange since OpenSSH 6.4", + "available since OpenSSH 7.4, Dropbear SSH 2018.76" + ] + } + }, + { + "algorithm":"curve25519-sha256@libssh.org", + "notes":{ + "info":[ + "default key exchange since OpenSSH 6.4", + "available since OpenSSH 6.4, Dropbear SSH 2013.62" + ] + } + }, + { + "algorithm":"ecdh-sha2-nistp256", + "notes":{ + "fail":[ + "using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency" + ], + "info":[ + "available since OpenSSH 5.7, Dropbear SSH 2013.62" + ] + } + }, + { + "algorithm":"ecdh-sha2-nistp384", + "notes":{ + "fail":[ + "using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency" + ], + "info":[ + "available since OpenSSH 5.7, Dropbear SSH 2013.62" + ] + } + }, + { + "algorithm":"ecdh-sha2-nistp521", + "notes":{ + "fail":[ + "using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency" + ], + "info":[ + "available since OpenSSH 5.7, Dropbear SSH 2013.62" + ] + } + }, + { + "algorithm":"sntrup761x25519-sha512@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 8.5" + ] + } + }, + { + "algorithm":"diffie-hellman-group-exchange-sha256", + "keysize":3072, + "notes":{ + "info":[ + "OpenSSH's GEX fallback mechanism was triggered during testing. Very old SSH clients will still be able to create connections using a 2048-bit modulus, though modern clients will use 3072. This can only be disabled by recompiling the code (see https://github.com/openssh/openssh-portable/blob/V_9_4/dh.c#L477).", + "available since OpenSSH 4.4" + ] + } + }, + { + "algorithm":"diffie-hellman-group16-sha512", + "notes":{ + "info":[ + "available since OpenSSH 7.3, Dropbear SSH 2016.73" + ] + } + }, + { + "algorithm":"diffie-hellman-group18-sha512", + "notes":{ + "info":[ + "available since OpenSSH 7.3" + ] + } + }, + { + "algorithm":"diffie-hellman-group14-sha256", + "notes":{ + "info":[ + "available since OpenSSH 7.3, Dropbear SSH 2016.73" + ], + "warn":[ + "2048-bit modulus only provides 112-bits of symmetric strength" + ] + } + } + ], + "key":[ + { + "algorithm":"rsa-sha2-512", + "keysize":3072, + "notes":{ + "info":[ + "available since OpenSSH 7.2" + ] + } + }, + { + "algorithm":"rsa-sha2-256", + "keysize":3072, + "notes":{ + "info":[ + "available since OpenSSH 7.2" + ] + } + }, + { + "algorithm":"ecdsa-sha2-nistp256", + "notes":{ + "fail":[ + "using elliptic curves that are suspected as being backdoored by the U.S. National Security Agency" + ], + "info":[ + "available since OpenSSH 5.7, Dropbear SSH 2013.62" + ], + "warn":[ + "using weak random number generator could reveal the key" + ] + } + }, + { + "algorithm":"ssh-ed25519", + "notes":{ + "info":[ + "available since OpenSSH 6.5" + ] + } + } + ], + "mac":[ + { + "algorithm":"umac-64-etm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ], + "warn":[ + "using small 64-bit tag size" + ] + } + }, + { + "algorithm":"umac-128-etm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"hmac-sha2-256-etm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"hmac-sha2-512-etm@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"hmac-sha1-etm@openssh.com", + "notes":{ + "fail":[ + "using broken SHA-1 hash algorithm" + ], + "info":[ + "available since OpenSSH 6.2" + ] + } + }, + { + "algorithm":"umac-64@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 4.7" + ], + "warn":[ + "using encrypt-and-MAC mode", + "using small 64-bit tag size" + ] + } + }, + { + "algorithm":"umac-128@openssh.com", + "notes":{ + "info":[ + "available since OpenSSH 6.2" + ], + "warn":[ + "using encrypt-and-MAC mode" + ] + } + }, + { + "algorithm":"hmac-sha2-256", + "notes":{ + "info":[ + "available since OpenSSH 5.9, Dropbear SSH 2013.56" + ], + "warn":[ + "using encrypt-and-MAC mode" + ] + } + }, + { + "algorithm":"hmac-sha2-512", + "notes":{ + "info":[ + "available since OpenSSH 5.9, Dropbear SSH 2013.56" + ], + "warn":[ + "using encrypt-and-MAC mode" + ] + } + }, + { + "algorithm":"hmac-sha1", + "notes":{ + "fail":[ + "using broken SHA-1 hash algorithm" + ], + "info":[ + "available since OpenSSH 2.1.0, Dropbear SSH 0.28" + ], + "warn":[ + "using encrypt-and-MAC mode" + ] + } + } + ], + "recommendations":{ + "critical":{ + "del":{ + "kex":[ + { + "name":"ecdh-sha2-nistp256", + "notes":"" + }, + { + "name":"ecdh-sha2-nistp384", + "notes":"" + }, + { + "name":"ecdh-sha2-nistp521", + "notes":"" + } + ], + "key":[ + { + "name":"ecdsa-sha2-nistp256", + "notes":"" + } + ], + "mac":[ + { + "name":"hmac-sha1", + "notes":"" + }, + { + "name":"hmac-sha1-etm@openssh.com", + "notes":"" + } + ] + } + }, + "warning":{ + "del":{ + "kex":[ + { + "name":"diffie-hellman-group14-sha256", + "notes":"" + } + ], + "mac":[ + { + "name":"hmac-sha2-256", + "notes":"" + }, + { + "name":"hmac-sha2-512", + "notes":"" + }, + { + "name":"umac-128@openssh.com", + "notes":"" + }, + { + "name":"umac-64-etm@openssh.com", + "notes":"" + }, + { + "name":"umac-64@openssh.com", + "notes":"" + } + ] + } + } + }, + "target":"1.1.1.1:22" + } \ No newline at end of file diff --git a/unittests/tools/test_ssh_audit_parser.py b/unittests/tools/test_ssh_audit_parser.py new file mode 100644 index 00000000000..10f070c31cb --- /dev/null +++ b/unittests/tools/test_ssh_audit_parser.py @@ -0,0 +1,32 @@ +from ..dojo_test_case import DojoTestCase +from dojo.tools.ssh_audit.parser import SSHAuditParser +from dojo.models import Test + + +class TestSSHAuditParser(DojoTestCase): + + def test_parse_file_with_many_vuln_has_many_findings(self): + testfile = open("unittests/scans/ssh_audit/many_vulns.json") + parser = SSHAuditParser() + findings = parser.get_findings(testfile, Test()) + for finding in findings: + for endpoint in finding.unsaved_endpoints: + endpoint.clean() + self.assertEqual(20, len(findings)) + self.assertEqual(findings[0].title, "SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2_CVE-2021-41617") + self.assertEqual(findings[1].title, "SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2_CVE-2020-15778") + self.assertEqual(findings[0].severity, "High") + self.assertEqual(findings[13].severity, "Medium") + + def test_parse_file_with_many_vuln_has_many_findings2(self): + testfile = open("unittests/scans/ssh_audit/many_vulns2.json") + parser = SSHAuditParser() + findings = parser.get_findings(testfile, Test()) + for finding in findings: + for endpoint in finding.unsaved_endpoints: + endpoint.clean() + self.assertEqual(12, len(findings)) + self.assertEqual(findings[0].title, "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.4_ecdh-sha2-nistp256") + self.assertEqual(findings[1].title, "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.4_ecdh-sha2-nistp384") + self.assertEqual(findings[0].severity, "High") + self.assertEqual(findings[9].severity, "Medium")