diff --git a/agent/exploits/cve_2024_11972.py b/agent/exploits/cve_2024_11972.py new file mode 100644 index 0000000..8f3d4b9 --- /dev/null +++ b/agent/exploits/cve_2024_11972.py @@ -0,0 +1,43 @@ +"""Agent Asteroid implementation for CVE-2024-11972""" + +import re + +from packaging import version + +from agent import definitions +from agent import exploits_registry +from agent.exploits import webexploit + +VERSION_PATTERN = re.compile(r"Stable tag:\s(\d+\.\d+(?:\.\d+)?)") +MAX_VULNERABLE_VERSION = version.parse("1.8.9") +VULNERABILITY_TITLE = ( + "Hunk Companion Plugin Unauthenticated Arbitrary Plugin Installation" +) +VULNERABILITY_REFERENCE = "CVE-2024-11972" +VULNERABILITY_DESCRIPTION = ( + "The Hunk Companion plugin for WordPress is vulnerable to an issue that allows " + "unauthenticated POST requests to install and activate plugins directly from the WordPress.org repository. " + "Versions below 1.9.0 are affected. Attackers could use this flaw to introduce arbitrary functionality " + "to a WordPress instance, potentially leading to severe consequences." +) +RISK_RATING = "CRITICAL" + + +@exploits_registry.register +class CVE202411972Exploit(webexploit.WebExploit): + accept_request = definitions.Request( + method="GET", path="/wp-content/plugins/hunk-companion/readme.txt" + ) + check_request = definitions.Request( + method="GET", path="/wp-content/plugins/hunk-companion/readme.txt" + ) + accept_pattern = [re.compile(r"====== Hunk Companion========")] + version_pattern = VERSION_PATTERN + vuln_ranges = [definitions.VulnRange(None, MAX_VULNERABLE_VERSION)] + + metadata = definitions.VulnerabilityMetadata( + title=VULNERABILITY_TITLE, + description=VULNERABILITY_DESCRIPTION, + reference=VULNERABILITY_REFERENCE, + risk_rating=RISK_RATING, + ) diff --git a/tests/exploits/cve_2024_11972_test.py b/tests/exploits/cve_2024_11972_test.py new file mode 100644 index 0000000..5bec4bb --- /dev/null +++ b/tests/exploits/cve_2024_11972_test.py @@ -0,0 +1,92 @@ +"""Unit tests for Agent Asteroid: CVE-2024-11972""" + +import requests_mock as req_mock +from ostorlab.agent.mixins import agent_report_vulnerability_mixin as vuln_mixin + +from agent import definitions +from agent.exploits import cve_2024_11972 + + +def testCVE202411972_whenVulnerable_reportFinding( + requests_mock: req_mock.mocker.Mocker, +) -> None: + """CVE-2024-11972 unit test: case when target is vulnerable.""" + requests_mock.get( + "http://localhost:80/wp-content/plugins/hunk-companion/readme.txt", + text=""" + ====== Hunk Companion======== + Contributors: hunkteam + Tags: companion, plugin + Requires at least: 4.7 + Tested up to: 6.7 + Requires PHP: 7.0 + Stable tag: 1.8.9 + License: GPLv2 + """, + status_code=200, + ) + exploit_instance = cve_2024_11972.CVE202411972Exploit() + target = definitions.Target("http", "localhost", 80) + + accept = exploit_instance.accept(target) + vulnerabilities = exploit_instance.check(target) + + assert accept is True + assert len(vulnerabilities) > 0 + vulnerability = vulnerabilities[0] + assert ( + vulnerability.entry.title + == "Hunk Companion Plugin Unauthenticated Arbitrary Plugin Installation" + ) + assert vulnerability.technical_detail == ( + "http://localhost:80 is vulnerable to CVE-2024-11972, " + "Hunk Companion Plugin Unauthenticated Arbitrary Plugin Installation" + ) + assert vulnerability.risk_rating == vuln_mixin.RiskRating.CRITICAL + + +def testCVE202411972_whenSafe_reportNothing( + requests_mock: req_mock.mocker.Mocker, +) -> None: + """CVE-2024-11972 unit test: case when target is safe.""" + exploit_instance = cve_2024_11972.CVE202411972Exploit() + requests_mock.get( + "http://localhost:80/wp-content/plugins/hunk-companion/readme.txt", + text=""" + ====== Hunk Companion======== + Contributors: hunkteam + Tags: companion, plugin + Requires at least: 4.7 + Tested up to: 6.7 + Requires PHP: 7.0 + Stable tag: 1.9.1 + License: GPLv2 + """, + status_code=200, + ) + target = definitions.Target("http", "localhost", 80) + + accept = exploit_instance.accept(target) + vulnerabilities = exploit_instance.check(target) + + assert accept is True + assert len(vulnerabilities) == 0 + + +def testCVE202411972_whenTargetNotHunkCompanion_reportNothing( + requests_mock: req_mock.mocker.Mocker, +) -> None: + """CVE-2024-11972 unit test: case when target is safe.""" + exploit_instance = cve_2024_11972.CVE202411972Exploit() + requests_mock.get( + "http://localhost:80/wp-content/plugins/hunk-companion/readme.txt", + text="""Not Found""", + status_code=404, + ) + target = definitions.Target("http", "localhost", 80) + + accept = exploit_instance.accept(target) + vulnerabilities = exploit_instance.check(target) + + assert accept is False + assert len(vulnerabilities) == 0