Skip to content

Commit

Permalink
Merge pull request #111 from Ostorlab/feature/cve_2024_40711
Browse files Browse the repository at this point in the history
  • Loading branch information
3asm authored Sep 13, 2024
2 parents b27d14c + f1d7702 commit 9a15095
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
84 changes: 84 additions & 0 deletions agent/exploits/cve_2024_40711.py
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
1 change: 1 addition & 0 deletions requirement.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ pygments
semantic_version
impacket
pysnmp==4.4.6
pyasn1==0.6.0
jaydebeapi
78 changes: 78 additions & 0 deletions tests/exploits/cve_2024_40711_test.py
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

0 comments on commit 9a15095

Please sign in to comment.