From ee702a89aa2e2f10dafaa361d96a55cc000d8566 Mon Sep 17 00:00:00 2001 From: nmasdoufi-ol Date: Fri, 13 Dec 2024 10:58:49 +0100 Subject: [PATCH 1/3] Add Detection for CVE-2024-11639 --- .../{cve_2024_8963.py => cve_2024_11639.py} | 23 +++++----- ...24_8963_test.py => cve_2024_11639_test.py} | 44 ++++++++++--------- 2 files changed, 33 insertions(+), 34 deletions(-) rename agent/exploits/{cve_2024_8963.py => cve_2024_11639.py} (70%) rename tests/exploits/{cve_2024_8963_test.py => cve_2024_11639_test.py} (65%) diff --git a/agent/exploits/cve_2024_8963.py b/agent/exploits/cve_2024_11639.py similarity index 70% rename from agent/exploits/cve_2024_8963.py rename to agent/exploits/cve_2024_11639.py index 2b6a06d..4cb4b1b 100644 --- a/agent/exploits/cve_2024_8963.py +++ b/agent/exploits/cve_2024_11639.py @@ -1,5 +1,3 @@ -"""Agent Asteroid implementation for CVE-2024-8963""" - import re import logging import datetime @@ -12,22 +10,21 @@ from agent import exploits_registry from agent import definitions - -VULNERABILITY_TITLE = "Path Traversal in Ivanti CSA" -VULNERABILITY_REFERENCE = "CVE-2024-8963" -VULNERABILITY_DESCRIPTION = """Path Traversal in the Ivanti CSA before 4.6 Patch 519 allows a remote unauthenticated attacker to access restricted functionality.""" +VULNERABILITY_TITLE = "Authentication Bypass in Ivanti CSA Admin Console" +VULNERABILITY_REFERENCE = "CVE-2024-11639" +VULNERABILITY_DESCRIPTION = """An authentication bypass in the admin web console of Ivanti CSA before 5.0.3 allows a remote unauthenticated attacker to gain administrative access.""" RISK_RATING = "CRITICAL" -"""Ivanti has released a security advisory for CVE-2024-8963, a critical vulnerability in Ivanti CSA 4.6 which was -incidentally addressed in its patch for CVE-2024-8190, which was released on September 10, 2024 (CSA 4.6 Patch 519).""" -PATCH_DATE = datetime.datetime(2024, 9, 10) +PATCH_DATE = datetime.datetime( + 2024, 12, 10, 17, 58, 17 +) # Based on Last Modified Date in advisory DEFAULT_TIMEOUT = 90 -HEADER_LAST_MODIFIED = "Last-Modified" +HEADER_IF_MODIFIED_SINCE = "Last-Modified" @exploits_registry.register -class CVE20248963Exploit(webexploit.WebExploit): +class CVE202411639Exploit(webexploit.WebExploit): accept_request = definitions.Request(method="GET", path="/") check_request = definitions.Request(method="GET", path="/allowed/ivanti-logo.png") accept_pattern = [ @@ -61,12 +58,12 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: logging.error("HTTP Request failed: %s", e) return [] - if HEADER_LAST_MODIFIED not in resp.headers: + if HEADER_IF_MODIFIED_SINCE not in resp.headers: return [] try: last_modified = datetime.datetime.strptime( - resp.headers[HEADER_LAST_MODIFIED], "%a, %d %b %Y %H:%M:%S %Z" + resp.headers[HEADER_IF_MODIFIED_SINCE], "%a, %d %b %Y %H:%M:%S %Z" ) except ValueError as e: logging.error("Couldn't parse date string and format: %s", e) diff --git a/tests/exploits/cve_2024_8963_test.py b/tests/exploits/cve_2024_11639_test.py similarity index 65% rename from tests/exploits/cve_2024_8963_test.py rename to tests/exploits/cve_2024_11639_test.py index 1a7a49c..9e15b5b 100644 --- a/tests/exploits/cve_2024_8963_test.py +++ b/tests/exploits/cve_2024_11639_test.py @@ -1,16 +1,16 @@ -"""Unit tests for Agent Asteroid: CVE-2024-8963""" +"""Unit tests for Agent Asteroid: CVE-2024-11639""" import requests import requests_mock as req_mock from agent import definitions -from agent.exploits import cve_2024_8963 +from agent.exploits import cve_2024_11639 -def testCVE20248963_whenVulnerable_reportFinding( +def testCVE202411639_whenVulnerable_reportFinding( requests_mock: req_mock.mocker.Mocker, ) -> None: - """CVE-2024-8963 unit test: case when target is vulnerable.""" + """CVE-2024-11639 unit test: case when target is vulnerable.""" vulnerable_date = "Fri, 03 Dec 2021 19:45:52 GMT" requests_mock.get( "http://localhost:80/", @@ -23,7 +23,7 @@ def testCVE20248963_whenVulnerable_reportFinding( content=b"PNG", status_code=200, ) - exploit_instance = cve_2024_8963.CVE20248963Exploit() + exploit_instance = cve_2024_11639.CVE202411639Exploit() target = definitions.Target("http", "localhost", 80) accept = exploit_instance.accept(target) @@ -31,25 +31,27 @@ def testCVE20248963_whenVulnerable_reportFinding( assert accept is True vulnerability = vulnerabilities[0] - assert vulnerability.entry.title == "Path Traversal in Ivanti CSA" + assert ( + vulnerability.entry.title == "Authentication Bypass in Ivanti CSA Admin Console" + ) assert vulnerability.technical_detail == ( - "http://localhost:80 is vulnerable to CVE-2024-8963, " - "Path Traversal in Ivanti CSA" + "http://localhost:80 is vulnerable to CVE-2024-11639, " + "Authentication Bypass in Ivanti CSA Admin Console" ) -def testCVE20248963_whenSafe_reportNothing( +def testCVE202411639_whenSafe_reportNothing( requests_mock: req_mock.mocker.Mocker, ) -> None: - """CVE-2024-8963 unit test: case when target is safe.""" - safe_date = "Fri, 03 Dec 2024 19:45:52 GMT" + """CVE-2024-11639 unit test: case when target is safe.""" + safe_date = "Fri, 11 Dec 2024 19:45:52 GMT" requests_mock.get( "http://localhost:80/allowed/ivanti-logo.png", headers={"Last-Modified": safe_date}, content=b"PNG", status_code=200, ) - exploit_instance = cve_2024_8963.CVE20248963Exploit() + exploit_instance = cve_2024_11639.CVE202411639Exploit() target = definitions.Target("http", "localhost", 80) vulnerabilities = exploit_instance.check(target) @@ -57,10 +59,10 @@ def testCVE20248963_whenSafe_reportNothing( assert len(vulnerabilities) == 0 -def testCVE20248963_whenInvalidLastModifiedFormat_doesNotCrash( +def testCVE202411639_whenInvalidIfModifiedSinceFormat_doesNotCrash( requests_mock: req_mock.mocker.Mocker, ) -> None: - """CVE-2024-8963 unit test: case when Last-Modified header has invalid format.""" + """CVE-2024-11639 unit test: case when Last-Modified header has invalid format.""" requests_mock.get( "http://localhost:80/", text="Ivanti(R) Cloud Services Appliance", @@ -72,7 +74,7 @@ def testCVE20248963_whenInvalidLastModifiedFormat_doesNotCrash( content=b"PNG", status_code=200, ) - exploit_instance = cve_2024_8963.CVE20248963Exploit() + exploit_instance = cve_2024_11639.CVE202411639Exploit() target = definitions.Target("http", "localhost", 80) accept = exploit_instance.accept(target) @@ -82,15 +84,15 @@ def testCVE20248963_whenInvalidLastModifiedFormat_doesNotCrash( assert len(vulnerabilities) == 0 -def testCVE20248963_whenRequestException_doesNotCrash( +def testCVE202411639_whenRequestException_doesNotCrash( requests_mock: req_mock.mocker.Mocker, ) -> None: - """CVE-2024-8963 unit test: case when a request exception occurs.""" + """CVE-2024-11639 unit test: case when a request exception occurs.""" requests_mock.get( "http://localhost:80/allowed/ivanti-logo.png", exc=requests.RequestException("Connection error"), ) - exploit_instance = cve_2024_8963.CVE20248963Exploit() + exploit_instance = cve_2024_11639.CVE202411639Exploit() target = definitions.Target("http", "localhost", 80) vulnerabilities = exploit_instance.check(target) @@ -98,16 +100,16 @@ def testCVE20248963_whenRequestException_doesNotCrash( assert len(vulnerabilities) == 0 -def testCVE20248963_whenNotIvantiCSA_doesNotAccept( +def testCVE202411639_whenNotIvantiCSA_doesNotAccept( requests_mock: req_mock.mocker.Mocker, ) -> None: - """CVE-2024-8963 unit test: case when target is not Ivanti CSA.""" + """CVE-2024-11639 unit test: case when target is not Ivanti CSA.""" requests_mock.get( "http://localhost:80/", text="Not Ivanti CSA", status_code=200, ) - exploit_instance = cve_2024_8963.CVE20248963Exploit() + exploit_instance = cve_2024_11639.CVE202411639Exploit() target = definitions.Target("http", "localhost", 80) accept = exploit_instance.accept(target) From 4405eb00d50c2389b2b6539582476e031194aa17 Mon Sep 17 00:00:00 2001 From: nmasdoufi-ol Date: Fri, 13 Dec 2024 18:07:43 +0100 Subject: [PATCH 2/3] Address Comments. --- agent/exploits/cve_2024_11639.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/exploits/cve_2024_11639.py b/agent/exploits/cve_2024_11639.py index 4cb4b1b..b6295b0 100644 --- a/agent/exploits/cve_2024_11639.py +++ b/agent/exploits/cve_2024_11639.py @@ -20,7 +20,7 @@ ) # Based on Last Modified Date in advisory DEFAULT_TIMEOUT = 90 -HEADER_IF_MODIFIED_SINCE = "Last-Modified" +HEADER_LAST_MODIFIED = "Last-Modified" @exploits_registry.register @@ -58,12 +58,12 @@ def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: logging.error("HTTP Request failed: %s", e) return [] - if HEADER_IF_MODIFIED_SINCE not in resp.headers: + if HEADER_LAST_MODIFIED not in resp.headers: return [] try: last_modified = datetime.datetime.strptime( - resp.headers[HEADER_IF_MODIFIED_SINCE], "%a, %d %b %Y %H:%M:%S %Z" + resp.headers[HEADER_LAST_MODIFIED], "%a, %d %b %Y %H:%M:%S %Z" ) except ValueError as e: logging.error("Couldn't parse date string and format: %s", e) From c3629fe125353400d2ae0f79c5f001a053a4a1aa Mon Sep 17 00:00:00 2001 From: nmasdoufi-ol Date: Fri, 13 Dec 2024 18:09:16 +0100 Subject: [PATCH 3/3] Address Comments. --- agent/exploits/cve_2024_11639.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agent/exploits/cve_2024_11639.py b/agent/exploits/cve_2024_11639.py index b6295b0..3b8c582 100644 --- a/agent/exploits/cve_2024_11639.py +++ b/agent/exploits/cve_2024_11639.py @@ -1,3 +1,5 @@ +"""Agent Asteroid implementation for CVE-2024-11639""" + import re import logging import datetime