diff --git a/boefjes/boefjes/katalogus/tests/integration/test_api.py b/boefjes/boefjes/katalogus/tests/integration/test_api.py index e54a91514c6..91f994e73de 100644 --- a/boefjes/boefjes/katalogus/tests/integration/test_api.py +++ b/boefjes/boefjes/katalogus/tests/integration/test_api.py @@ -49,9 +49,9 @@ def test_get_local_plugin(self): def test_filter_plugins(self): response = self.client.get(f"/v1/organisations/{self.org.id}/plugins/") - self.assertEqual(len(response.json()), 93) + self.assertEqual(len(response.json()), 95) response = self.client.get(f"/v1/organisations/{self.org.id}/plugins?plugin_type=boefje") - self.assertEqual(len(response.json()), 41) + self.assertEqual(len(response.json()), 42) response = self.client.get(f"/v1/organisations/{self.org.id}/plugins?limit=10") self.assertEqual(len(response.json()), 10) @@ -76,7 +76,7 @@ def test_add_boefje(self): self.assertEqual(response.status_code, 422) response = self.client.get(f"/v1/organisations/{self.org.id}/plugins/?plugin_type=boefje") - self.assertEqual(len(response.json()), 42) + self.assertEqual(len(response.json()), 43) boefje_dict = boefje.dict() boefje_dict["consumes"] = list(boefje_dict["consumes"]) @@ -101,7 +101,7 @@ def test_add_normalizer(self): self.assertEqual(response.status_code, 201) response = self.client.get(f"/v1/organisations/{self.org.id}/plugins/?plugin_type=normalizer") - self.assertEqual(len(response.json()), 53) + self.assertEqual(len(response.json()), 54) response = self.client.get(f"/v1/organisations/{self.org.id}/plugins/test_normalizer") self.assertEqual(response.json(), normalizer.dict()) diff --git a/boefjes/boefjes/plugins/kat_cve_2024_6387/__init__.py b/boefjes/boefjes/plugins/kat_cve_2024_6387/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/boefjes/boefjes/plugins/kat_cve_2024_6387/normalize.py b/boefjes/boefjes/plugins/kat_cve_2024_6387/normalize.py new file mode 100644 index 00000000000..0948823cecc --- /dev/null +++ b/boefjes/boefjes/plugins/kat_cve_2024_6387/normalize.py @@ -0,0 +1,68 @@ +""" +CVE-2024-6387 checker +Author: Mischa van Geelen <@rickgeex> + +""" + +from collections.abc import Iterable + +from boefjes.job_models import NormalizerOutput +from octopoes.models import Reference +from octopoes.models.ooi.findings import CVEFindingType, Finding +from packaging.version import Version + +VULNERABLE_VERSIONS = [ + "SSH-2.0-OpenSSH_8.5", + "SSH-2.0-OpenSSH_8.6", + "SSH-2.0-OpenSSH_8.7", + "SSH-2.0-OpenSSH_8.8", + "SSH-2.0-OpenSSH_8.9", + "SSH-2.0-OpenSSH_9.0", + "SSH-2.0-OpenSSH_9.1", + "SSH-2.0-OpenSSH_9.2", + "SSH-2.0-OpenSSH_9.3", + "SSH-2.0-OpenSSH_9.4", + "SSH-2.0-OpenSSH_9.5", + "SSH-2.0-OpenSSH_9.6", + "SSH-2.0-OpenSSH_9.7", +] + + +def is_vulnerable(banner: str) -> bool: + if not any(version in banner for version in VULNERABLE_VERSIONS): + return False + + if banner.startswith("SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u"): + _, security_update = banner.split("deb12u") + if Version(security_update) >= Version("3"): + return False + elif banner.startswith("SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu"): + _, security_update = banner.split("3ubuntu") + if Version(security_update) >= Version("13.3"): + return False + elif banner.startswith("SSH-2.0-OpenSSH_9.3p1 Ubuntu-1ubuntu"): + _, security_update = banner.split("1ubuntu") + if Version(security_update) >= Version("3.6"): + return False + elif banner.startswith("SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu"): + _, security_update = banner.split("3ubuntu") + if Version(security_update) >= Version("0.10"): + return False + + return True + + +def run(input_ooi: dict, raw: bytes) -> Iterable[NormalizerOutput]: + ooi = Reference.from_str(input_ooi["primary_key"]) + + banner = raw.decode() + + if banner.startswith("SSH-2.0-OpenSSH") and is_vulnerable(banner): + finding_type = CVEFindingType(id="CVE-2024-6387") + finding = Finding( + finding_type=finding_type.reference, + ooi=ooi, + description="Service is most likely vulnerable to CVE-2024-6387", + ) + yield finding_type + yield finding diff --git a/boefjes/boefjes/plugins/kat_cve_2024_6387/normalizer.json b/boefjes/boefjes/plugins/kat_cve_2024_6387/normalizer.json new file mode 100644 index 00000000000..1cf4c49e5bf --- /dev/null +++ b/boefjes/boefjes/plugins/kat_cve_2024_6387/normalizer.json @@ -0,0 +1,11 @@ +{ + "id": "kat_cve_2024_6387_normalize", + "consumes": [ + "openkat/service-banner" + ], + "description": "Checks service banner for CVE-2024-6387, enable service banner boefje to get the service banner", + "produces": [ + "Finding", + "CVEFindingType" + ] +} diff --git a/boefjes/boefjes/plugins/kat_service_banner/__init__.py b/boefjes/boefjes/plugins/kat_service_banner/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/boefjes/boefjes/plugins/kat_service_banner/boefje.json b/boefjes/boefjes/plugins/kat_service_banner/boefje.json new file mode 100644 index 00000000000..6319afe8c94 --- /dev/null +++ b/boefjes/boefjes/plugins/kat_service_banner/boefje.json @@ -0,0 +1,9 @@ +{ + "id": "service_banner", + "name": "Service banner download", + "description": "Downloads service banners from the target hosts", + "consumes": [ + "IPPort" + ], + "scan_level": 2 +} diff --git a/boefjes/boefjes/plugins/kat_service_banner/cover.jpg b/boefjes/boefjes/plugins/kat_service_banner/cover.jpg new file mode 100644 index 00000000000..753c6d69218 Binary files /dev/null and b/boefjes/boefjes/plugins/kat_service_banner/cover.jpg differ diff --git a/boefjes/boefjes/plugins/kat_service_banner/main.py b/boefjes/boefjes/plugins/kat_service_banner/main.py new file mode 100644 index 00000000000..fdc791290c6 --- /dev/null +++ b/boefjes/boefjes/plugins/kat_service_banner/main.py @@ -0,0 +1,41 @@ +import socket + +from boefjes.job_models import BoefjeMeta + +TIMEOUT = 1.0 + + +def get_sock(ip, port, timeout): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(timeout) + try: + sock.connect((ip, port)) + return sock + except Exception: + return None + + +def get_banner(sock): + if not sock: + return [({"boefje/error"}, "Unable to connect to the service")] + try: + sock.settimeout(TIMEOUT) + banner = sock.recv(1024) + try: + banner = banner.decode().strip() + except UnicodeDecodeError: + banner = banner.decode("latin1").strip() + sock.close() + return [({"openkat/service-banner"}, banner)] + except Exception as e: + return [({"boefje/error"}, f"Unable to get banner. {str(e)}")] + + +def run(boefje_meta: BoefjeMeta) -> list[tuple[set, str | bytes]]: + input_ = boefje_meta.arguments["input"] # input is IPPort + port = input_["port"] + ip = input_["address"]["address"] + + sock = get_sock(ip, port, TIMEOUT) + + return get_banner(sock) diff --git a/boefjes/tests/test_cve-2024-6387.py b/boefjes/tests/test_cve-2024-6387.py new file mode 100644 index 00000000000..71a42490000 --- /dev/null +++ b/boefjes/tests/test_cve-2024-6387.py @@ -0,0 +1,23 @@ +from boefjes.plugins.kat_cve_2024_6387.normalize import is_vulnerable + + +def test_is_vulnerable(): + for version in [ + "SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u3", + "SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10", + "SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.3", + "SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.4", + "SSH-2.0-OpenSSH_9.3p1 Ubuntu-1ubuntu3.6", + "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10", + ]: + assert not is_vulnerable(version) + + for version in [ + "SSH-2.0-OpenSSH_8.9p1", + "SSH-2.0-OpenSSH_9.2p1", + "SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2", + "SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13", + "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7", + "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3", + ]: + assert is_vulnerable(version)