Made changes following suggestion

1. Change the date format from d/m/Y to Y-m-d (e.g. 2004-08-28)

2. In the return/output of ssl_certificate_scan in SSL library please  add certificate "subject" and "issuer" so these could be logged

3. Rename ssl_version module to ssl_weak_version

4. Change ssl_expired_certificate module to return expired certs only ( do not count expiring_soon certs - it is not a vulnerability!)

5. Create a separate ssl_expiring_certificate module in modules/scan (remember  'expiring soon'  is not a vulnerability, so we need to make this a 'scan' module)

6. Rename ssl_signed_certificate module to ssl_certificate_weak_signature and remove the self-signed check from it

7. Create a separate ssl_self_signed_certificate module in modules/vuln

Next in  nettacker/core/lib/ssl.py
 in class SslLibrary(BaseLibrary): you have ssl_certificate_scan and ssl_version_and_cipher_scan methods.
 There is a common code in these two methods so these could be refactored to remove the repetition.  Please refactor/improve this.
 In ssl_version_and_cipher_scan also please add add  to the output /return certificate "subject" ,"issuer" and an expiry date.
 This way if a user scans they network using IP addresses and some servers will come up with weak SSL versions/ciphers it will be easier for user to identify the servers using the certificate subject/issuer
This commit is contained in:
Captain-T2004 2024-08-31 02:42:15 +05:30
parent 5518b140f6
commit e47ef52929
10 changed files with 181 additions and 75 deletions

View File

@ -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)),

View File

@ -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()

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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
reverse: false

View File

@ -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
reverse: false
issuer:
reverse: false
subject:
reverse: false
expiration_date:
reverse: false

View File

@ -1,4 +1,5 @@
from unittest.mock import patch
from nettacker.core.lib.socket import create_tcp_socket, SocketEngine
from tests.common import TestCase

View File

@ -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), []
)