generated from Ostorlab/template_agent
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #111 from Ostorlab/feature/cve_2024_40711
Add CVE-2024-40711
- Loading branch information
Showing
3 changed files
with
163 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
"""Agent Asteroid implementation for CVE-2024-40711""" | ||
|
||
import logging | ||
import re | ||
from packaging import version | ||
import requests | ||
from requests import exceptions as requests_exceptions | ||
from urllib import parse as urlparse | ||
|
||
from agent.exploits import webexploit | ||
from agent import exploits_registry | ||
from agent import definitions | ||
|
||
VULNERABILITY_TITLE = "Veeam Backup & Replication Remote Code Execution Vulnerability" | ||
VULNERABILITY_REFERENCE = "CVE-2024-40711" | ||
VULNERABILITY_DESCRIPTION = """A critical remote code execution (RCE) vulnerability in Veeam Backup & Replication (VBR) | ||
that can be exploited without authentication. This vulnerability affects VBR version 12.1.2.172 | ||
and all earlier versions of the 12 branch.""" | ||
RISK_RATING = "CRITICAL" | ||
MAX_VULNERABLE_VERSION = version.parse("12.1.2.172") | ||
|
||
MAX_REDIRECTS = 2 | ||
DEFAULT_TIMEOUT = 90 | ||
|
||
|
||
@exploits_registry.register | ||
class CVE202440711Exploit(webexploit.WebExploit): | ||
accept_request = definitions.Request(method="GET", path="/api/v1/serverInfo") | ||
check_request = definitions.Request( | ||
method="GET", path="/api/v1/serverInfo", headers={"X-Api-Version": "1.1-rev1"} | ||
) | ||
accept_pattern = [re.compile(r'"name"\s*:\s*"Veeam Backup and Replication"')] | ||
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 specific vulnerability on a specific target. | ||
Args: | ||
target: Target to scan | ||
Returns: | ||
List of identified vulnerabilities. | ||
""" | ||
session = requests.Session() | ||
session.max_redirects = MAX_REDIRECTS | ||
session.verify = False | ||
|
||
vulnerabilities: list[definitions.Vulnerability] = [] | ||
|
||
target_endpoint = urlparse.urljoin(target.origin, self.check_request.path) | ||
|
||
try: | ||
req = requests.Request( | ||
method=self.check_request.method, | ||
url=target_endpoint, | ||
headers=self.check_request.headers, | ||
data=self.check_request.data, | ||
).prepare() | ||
resp = session.send(req, timeout=DEFAULT_TIMEOUT) | ||
except requests_exceptions.RequestException as e: | ||
logging.info("Request Exception Occured: %s", e) | ||
return vulnerabilities | ||
|
||
try: | ||
response_json = resp.json() | ||
extracted_version = version.parse(response_json.get("buildVersion", "")) | ||
except version.InvalidVersion as e: | ||
logging.info("No version was extracted: %s", e) | ||
return vulnerabilities | ||
|
||
if ( | ||
extracted_version <= MAX_VULNERABLE_VERSION | ||
and response_json.get("name") == "Veeam Backup and Replication" | ||
and extracted_version.major == 12 | ||
): | ||
vulnerability = self._create_vulnerability(target) | ||
vulnerabilities.append(vulnerability) | ||
|
||
return vulnerabilities |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,4 +9,5 @@ pygments | |
semantic_version | ||
impacket | ||
pysnmp==4.4.6 | ||
pyasn1==0.6.0 | ||
jaydebeapi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
"""Unit tests for Agent Asteroid: CVE_2024_40711""" | ||
|
||
import requests_mock as req_mock | ||
import requests | ||
|
||
from agent import definitions | ||
from agent.exploits import cve_2024_40711 | ||
|
||
|
||
def testCVE202440711_whenVulnerable_reportFinding( | ||
requests_mock: req_mock.mocker.Mocker, | ||
) -> None: | ||
"""CVE_2024_40711 unit test: case when target is vulnerable.""" | ||
requests_mock.get( | ||
"http://localhost:80/api/v1/serverInfo", | ||
json={"name": "Veeam Backup and Replication", "buildVersion": "12.1.2.172"}, | ||
status_code=200, | ||
) | ||
exploit_instance = cve_2024_40711.CVE202440711Exploit() | ||
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 | ||
== "Veeam Backup & Replication Remote Code Execution Vulnerability" | ||
) | ||
assert vulnerability.technical_detail == ( | ||
"http://localhost:80 is vulnerable to CVE-2024-40711, Veeam Backup & Replication Remote Code Execution " | ||
"Vulnerability" | ||
) | ||
|
||
|
||
def testCVE202443044_whenSafeVersion_reportNothing( | ||
requests_mock: req_mock.mocker.Mocker, | ||
) -> None: | ||
"""CVE_2024_40711 unit test: case when target is safe.""" | ||
|
||
requests_mock.get( | ||
"http://localhost:80/api/v1/serverInfo", | ||
json={ | ||
"type": "https://httpstatuses.com/404", | ||
"title": "Not Found", | ||
"status": 404, | ||
"extensions": {}, | ||
}, | ||
status_code=404, | ||
) | ||
exploit_instance = cve_2024_40711.CVE202440711Exploit() | ||
target = definitions.Target("http", "localhost", 80) | ||
|
||
accept = exploit_instance.accept(target) | ||
vulnerabilities = exploit_instance.check(target) | ||
|
||
assert accept is False | ||
assert len(vulnerabilities) == 0 | ||
|
||
|
||
def testCVE202440711_whenRequestException_reportNothing( | ||
requests_mock: req_mock.mocker.Mocker, | ||
) -> None: | ||
"""CVE_2024_40711 unit test: case when a request exception occurs.""" | ||
|
||
requests_mock.get( | ||
"http://localhost:80/api/v1/serverInfo", | ||
exc=requests.RequestException("Mocked network error"), | ||
) | ||
|
||
exploit_instance = cve_2024_40711.CVE202440711Exploit() | ||
target = definitions.Target("http", "localhost", 80) | ||
|
||
vulnerabilities = exploit_instance.check(target) | ||
|
||
assert len(vulnerabilities) == 0 |