diff --git a/nettacker/core/lib/ssl.py b/nettacker/core/lib/ssl.py index 1628a7fd..4ef2816f 100644 --- a/nettacker/core/lib/ssl.py +++ b/nettacker/core/lib/ssl.py @@ -18,14 +18,19 @@ def is_weak_hash_algo(algo): return False +def create_socket_connection(context, host, port, timeout): + socket_connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + socket_connection.settimeout(timeout) + socket_connection.connect((host, port)) + socket_connection = context.wrap_socket(socket_connection, server_hostname=host) + return socket_connection + + def is_weak_ssl_version(host, port, timeout): def test_ssl_version(host, port, timeout, ssl_version=None): try: context = ssl.SSLContext(ssl_version) - socket_connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - socket_connection.settimeout(timeout) - socket_connection.connect((host, port)) - socket_connection = context.wrap_socket(socket_connection, server_hostname=host) + socket_connection = create_socket_connection(context, host, port, timeout) return socket_connection.version() except ssl.SSLError: @@ -43,7 +48,7 @@ def is_weak_ssl_version(host, port, timeout): supported_versions = [] lowest_version = "" for ssl_version in ssl_versions: - version = test_ssl_verison(host, port, timeout, ssl_version=ssl_version) + version = test_ssl_version(host, port, timeout, ssl_version=ssl_version) if version: lowest_version = version supported_versions.append(version) @@ -58,11 +63,7 @@ def is_weak_cipher_suite(host, port, timeout): context.check_hostname = False context.verify_mode = ssl.CERT_NONE context.set_ciphers(cipher) - - socket_connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - socket_connection.settimeout(timeout) - socket_connection.connect((host, port)) - socket_connection = context.wrap_socket(socket_connection, server_hostname=host) + create_socket_connection(context, host, port, timeout) return True except ssl.SSLError: @@ -126,6 +127,25 @@ def create_tcp_socket(host, port, timeout): return socket_connection, ssl_flag +def get_cert_info(cert): + x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert) + weak_signing_algo = is_weak_hash_algo(str(x509.get_signature_algorithm())) + cert_activation = datetime.strptime(x509.get_notBefore().decode("utf-8"), "%Y%m%d%H%M%S%z") + cert_expires = datetime.strptime(x509.get_notAfter().decode("utf-8"), "%Y%m%d%H%M%S%z") + return { + "expired": x509.has_expired(), + "self_signed": x509.get_issuer() == x509.get_subject(), + "issuer": str(x509.get_issuer()), + "subject": str(x509.get_subject()), + "signing_algo": str(x509.get_signature_algorithm()), + "weak_signing_algo": weak_signing_algo, + "activation_date": cert_activation.strftime("%Y/%m/%d"), + "not_activated": (cert_activation - datetime.now(timezone.utc)).days > 0, + "expiration_date": cert_expires.strftime("%Y/%m/%d"), + "expiring_soon": (cert_expires - datetime.now(timezone.utc)).days < 30, + } + + class SslLibrary(BaseLibrary): def ssl_certificate_scan(self, host, port, timeout): tcp_socket = create_tcp_socket(host, port, timeout) @@ -134,34 +154,20 @@ class SslLibrary(BaseLibrary): socket_connection, ssl_flag = tcp_socket peer_name = socket_connection.getpeername() - if ssl_flag: - cert = ssl.get_server_certificate((host, port)) - x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert) - weak_signing_algo = is_weak_hash_algo(str(x509.get_signature_algorithm())) - cert_activation = datetime.strptime( - x509.get_notBefore().decode("utf-8"), "%Y%m%d%H%M%S%z" - ) - cert_expires = datetime.strptime(x509.get_notAfter().decode("utf-8"), "%Y%m%d%H%M%S%z") - - return { - "expired": x509.has_expired(), - "self_signed": x509.get_issuer() == x509.get_subject(), - "signing_algo": str(x509.get_signature_algorithm()), - "weak_signing_algo": weak_signing_algo, - "activation_date": cert_activation.strftime("%d/%m/%Y"), - "not_activated": (cert_activation - datetime.now(timezone.utc)).days > 0, - "expiration_date": cert_expires.strftime("%d/%m/%Y"), - "expiring_soon": (cert_expires - datetime.now(timezone.utc)).days < 30, - "ssl_flag": ssl_flag, - "peer_name": peer_name, - "service": socket.getservbyport(int(port)), - } - return { + scan_info = { "ssl_flag": ssl_flag, "peer_name": peer_name, "service": socket.getservbyport(int(port)), } + if ssl_flag: + cert = ssl.get_server_certificate((host, port)) + cert_info = get_cert_info(cert) + scan_info = cert_info | scan_info + return scan_info + + return scan_info + def ssl_version_and_cipher_scan(self, host, port, timeout): tcp_socket = create_tcp_socket(host, port, timeout) if tcp_socket is None: @@ -171,6 +177,11 @@ class SslLibrary(BaseLibrary): peer_name = socket_connection.getpeername() if ssl_flag: + try: + cert = ssl.get_server_certificate((host, port)) + except ssl.SSLError: + cert = None + cert_info = get_cert_info(cert) if cert else None ssl_ver, weak_version = is_weak_ssl_version(host, port, timeout) cipher_suite, weak_cipher_suite = is_weak_cipher_suite(host, port, timeout) @@ -179,6 +190,9 @@ class SslLibrary(BaseLibrary): "weak_version": weak_version, "cipher_suite": cipher_suite, "weak_cipher_suite": weak_cipher_suite, + "issuer": cert_info["issuer"] if cert_info else "NA", + "subject": cert_info["subject"] if cert_info else "NA", + "expiration_date": cert_info["expiration_date"] if cert_info else "NA", "ssl_flag": ssl_flag, "peer_name": peer_name, "service": socket.getservbyport(int(port)), diff --git a/nettacker/core/module.py b/nettacker/core/module.py index 120ca759..337115e2 100644 --- a/nettacker/core/module.py +++ b/nettacker/core/module.py @@ -49,10 +49,12 @@ class Module: "subdomain_scan", "icmp_scan", "port_scan", - "ssl_version_vuln", + "ssl_weak_version_vuln", "ssl_weak_cipher_vuln", - "ssl_signed_certificate_vuln", + "ssl_certificate_weak_signature_vuln", + "ssl_self_signed_certificate_vuln", "ssl_expired_certificate_vuln", + "ssl_expiring_certificate_scan", ] contents = TemplateLoader("port_scan", {"target": ""}).load() diff --git a/nettacker/modules/scan/ssl_expiring_certificate.yaml b/nettacker/modules/scan/ssl_expiring_certificate.yaml new file mode 100644 index 00000000..055dc44f --- /dev/null +++ b/nettacker/modules/scan/ssl_expiring_certificate.yaml @@ -0,0 +1,37 @@ +info: + name: ssl_expiring_certificate_scan + author: Captain-T2004 + severity: 6 + description: check if the ssl certificate is expiring soon + reference: + - https://www.beyondsecurity.com/resources/vulnerabilities/ssl-certificate-expiry + profiles: + - scan + - ssl + +payloads: + - library: ssl + steps: + - method: ssl_certificate_scan + timeout: 3 + host: "{target}" + ports: + - 21 + - 25 + - 110 + - 143 + - 443 + - 587 + - 990 + - 1080 + - 8080 + response: + condition_type: or + conditions: + grouped_conditions: + condition_type: and + conditions: + expiring_soon: + reverse: false + expiration_date: + reverse: false diff --git a/nettacker/modules/vuln/ssl_signed_certificate.yaml b/nettacker/modules/vuln/ssl_certificate_weak_signature.yaml similarity index 90% rename from nettacker/modules/vuln/ssl_signed_certificate.yaml rename to nettacker/modules/vuln/ssl_certificate_weak_signature.yaml index f2e4c10d..75d41e6e 100644 --- a/nettacker/modules/vuln/ssl_signed_certificate.yaml +++ b/nettacker/modules/vuln/ssl_certificate_weak_signature.yaml @@ -1,5 +1,5 @@ info: - name: ssl_certificate_vuln + name: ssl_certificate_weak_signature_vuln author: Captain-T2004 severity: 6 description: check if there are any ssl_certificate vulnerabilities present @@ -28,8 +28,6 @@ payloads: response: condition_type: or conditions: - self_signed: - reverse: false grouped_conditions: condition_type: and conditions: diff --git a/nettacker/modules/vuln/ssl_expired_certificate.yaml b/nettacker/modules/vuln/ssl_expired_certificate.yaml index 576af037..6c81503d 100644 --- a/nettacker/modules/vuln/ssl_expired_certificate.yaml +++ b/nettacker/modules/vuln/ssl_expired_certificate.yaml @@ -1,5 +1,5 @@ info: - name: ssl_certificate_vuln + name: ssl_expired_certificate_vuln author: Captain-T2004 severity: 6 description: check if there are any ssl_certificate vulnerabilities present @@ -36,13 +36,6 @@ payloads: expiration_date: reverse: false grouped_conditions_2: - condition_type: and - conditions: - expiring_soon: - reverse: false - expiration_date: - reverse: false - grouped_conditions_3: condition_type: and conditions: not_activated: diff --git a/nettacker/modules/vuln/ssl_self_signed_certificate.yaml b/nettacker/modules/vuln/ssl_self_signed_certificate.yaml new file mode 100644 index 00000000..fe406b43 --- /dev/null +++ b/nettacker/modules/vuln/ssl_self_signed_certificate.yaml @@ -0,0 +1,39 @@ +info: + name: ssl_self_signed_certificate_vuln + author: Captain-T2004 + severity: 6 + description: check if the ssl certificate is self-signed + reference: + - https://www.ssl.com/article/ssl-tls-self-signed-certificates/ + profiles: + - scan + - ssl + +payloads: + - library: ssl + steps: + - method: ssl_certificate_scan + timeout: 3 + host: "{target}" + ports: + - 21 + - 25 + - 110 + - 143 + - 443 + - 587 + - 990 + - 1080 + - 8080 + response: + condition_type: or + conditions: + grouped_conditions: + condition_type: and + conditions: + self_signed: + reverse: false + issuer: + reverse: false + subject: + reverse: false \ No newline at end of file diff --git a/nettacker/modules/vuln/ssl_weak_cipher.yaml b/nettacker/modules/vuln/ssl_weak_cipher.yaml index f23ecb52..ecda9812 100644 --- a/nettacker/modules/vuln/ssl_weak_cipher.yaml +++ b/nettacker/modules/vuln/ssl_weak_cipher.yaml @@ -1,5 +1,5 @@ info: - name: ssl_version_vuln + name: ssl_weak_cipher_vuln author: Captain-T2004 severity: 6 description: check if ssl version is unsafe or uses any bad ciphers. @@ -35,5 +35,4 @@ payloads: weak_cipher_suite: reverse: false cipher_suite: - reverse: false - \ No newline at end of file + reverse: false \ No newline at end of file diff --git a/nettacker/modules/vuln/ssl_version.yaml b/nettacker/modules/vuln/ssl_weak_version.yaml similarity index 77% rename from nettacker/modules/vuln/ssl_version.yaml rename to nettacker/modules/vuln/ssl_weak_version.yaml index 69fbcceb..26f7dd64 100644 --- a/nettacker/modules/vuln/ssl_version.yaml +++ b/nettacker/modules/vuln/ssl_weak_version.yaml @@ -1,5 +1,5 @@ info: - name: ssl_version_vuln + name: ssl_weak_version_vuln author: Captain-T2004 severity: 6 description: check if ssl version is unsafe or uses any bad ciphers. @@ -35,4 +35,10 @@ payloads: weak_version: reverse: false ssl_version: - reverse: false \ No newline at end of file + reverse: false + issuer: + reverse: false + subject: + reverse: false + expiration_date: + reverse: false diff --git a/tests/core/test_socket.py b/tests/core/test_socket.py index 7a53cc75..f6ad745c 100644 --- a/tests/core/test_socket.py +++ b/tests/core/test_socket.py @@ -1,4 +1,5 @@ from unittest.mock import patch + from nettacker.core.lib.socket import create_tcp_socket, SocketEngine from tests.common import TestCase diff --git a/tests/core/test_ssl.py b/tests/core/test_ssl.py index 7e892ac5..700e4bc2 100644 --- a/tests/core/test_ssl.py +++ b/tests/core/test_ssl.py @@ -53,27 +53,30 @@ class Mockx509Object: class Responses: - ssl_version_vuln = { + ssl_weak_version_vuln = { "ssl_version": ["TLSv1"], "weak_version": True, "ssl_flag": True, + "issuer": "test_issuer", + "subject": "test_subject", + "expiration_date": "2100/12/07", } ssl_certificate_expired = { "expired": True, - "expiration_date": "07/12/2023", + "expiration_date": "2023/12/07", "not_activated": False, - "activation_date": "07/12/2023", + "activation_date": "2023/12/07", "expiring_soon": True, "ssl_flag": True, } ssl_certificate_deactivated = { "expired": False, - "expiration_date": "07/12/2100", + "expiration_date": "2100/12/07", "expiring_soon": False, "not_activated": True, - "activation_date": "07/12/2100", + "activation_date": "2100/12/07", "ssl_flag": True, } @@ -81,7 +84,7 @@ class Responses: class Substeps: - ssl_version_vuln = { + ssl_weak_version_vuln = { "method": "ssl_version_and_cipher_scan", "response": { "condition_type": "or", @@ -91,6 +94,9 @@ class Substeps: "conditions": { "weak_version": {"reverse": False}, "ssl_version": {"reverse": False}, + "issuer": {"reverse": False}, + "subject": {"reverse": False}, + "expiration_date": {"reverse": False}, }, } }, @@ -110,13 +116,6 @@ class Substeps: }, }, "grouped_conditions_2": { - "condition_type": "and", - "conditions": { - "expiring_soon": {"reverse": False}, - "expiration_date": {"reverse": False}, - }, - }, - "grouped_conditions_3": { "condition_type": "and", "conditions": { "not_activated": {"reverse": False}, @@ -164,6 +163,9 @@ class TestSocketMethod(TestCase): "peer_name": "example.com", "cipher_suite": ["HIGH"], "weak_cipher_suite": False, + "issuer": "NA", + "subject": "NA", + "expiration_date": "NA", }, ) @@ -180,6 +182,9 @@ class TestSocketMethod(TestCase): "peer_name": "example.com", "cipher_suite": ["LOW"], "weak_cipher_suite": True, + "issuer": "NA", + "subject": "NA", + "expiration_date": "NA", }, ) @@ -223,10 +228,12 @@ class TestSocketMethod(TestCase): "ssl_flag": True, "service": "http", "self_signed": False, + "issuer": "test_issuer", + "subject": "test_subject", "expiring_soon": False, - "expiration_date": "07/12/2100", + "expiration_date": "2100/12/07", "not_activated": False, - "activation_date": "07/12/2023", + "activation_date": "2023/12/07", "signing_algo": "test_algo", "weak_signing_algo": False, "peer_name": "example.com", @@ -250,10 +257,12 @@ class TestSocketMethod(TestCase): "ssl_flag": True, "service": "http", "self_signed": True, + "issuer": "test_issuer_subject", + "subject": "test_issuer_subject", "expiring_soon": False, - "expiration_date": "07/12/2100", + "expiration_date": "2100/12/07", "not_activated": True, - "activation_date": "07/12/2100", + "activation_date": "2100/12/07", "signing_algo": "test_algo", "weak_signing_algo": True, "peer_name": "example.com", @@ -358,7 +367,7 @@ class TestSocketMethod(TestCase): engine.response_conditions_matched( Substep.ssl_expired_certificate_scan, Response.ssl_certificate_expired ), - {"expired": True, "expiration_date": "07/12/2023", "expiring_soon": True}, + {"expired": True, "expiration_date": "2023/12/07"}, ) # ssl_certificate_scan_not_activated self.assertEqual( @@ -366,21 +375,29 @@ class TestSocketMethod(TestCase): Substep.ssl_expired_certificate_scan, Response.ssl_certificate_deactivated, ), - {"not_activated": True, "activation_date": "07/12/2100"}, + {"not_activated": True, "activation_date": "2100/12/07"}, ) - # ssl_version_vuln + # ssl_weak_version_vuln self.assertEqual( engine.response_conditions_matched( - Substep.ssl_version_vuln, Response.ssl_version_vuln + Substep.ssl_weak_version_vuln, Response.ssl_weak_version_vuln ), - {"weak_version": True, "ssl_version": ["TLSv1"]}, + { + "weak_version": True, + "ssl_version": ["TLSv1"], + "issuer": "test_issuer", + "subject": "test_subject", + "expiration_date": "2100/12/07", + }, ) # ssl_* scans with ssl_flag = False self.assertEqual( - engine.response_conditions_matched(Substep.ssl_version_vuln, Response.ssl_off), [] + engine.response_conditions_matched(Substep.ssl_weak_version_vuln, Response.ssl_off), [] ) # * scans with response None i.e. TCP connection failed(None) - self.assertEqual(engine.response_conditions_matched(Substep.ssl_version_vuln, None), []) + self.assertEqual( + engine.response_conditions_matched(Substep.ssl_weak_version_vuln, None), [] + )