forked from p15670423/monkey
Merge pull request #2088 from guardicore/1662-remove-configservice-from-reportservice
1662 remove configservice from reportservice
This commit is contained in:
commit
753970f644
|
@ -2,9 +2,6 @@ SSH_KEYS_PATH = ["internal", "exploits", "exploit_ssh_keys"]
|
|||
INACCESSIBLE_SUBNETS_PATH = ["basic_network", "network_analysis", "inaccessible_subnets"]
|
||||
USER_LIST_PATH = ["basic", "credentials", "exploit_user_list"]
|
||||
PASSWORD_LIST_PATH = ["basic", "credentials", "exploit_password_list"]
|
||||
EXPLOITER_CLASSES_PATH = ["basic", "exploiters", "exploiter_classes"]
|
||||
SUBNET_SCAN_LIST_PATH = ["basic_network", "scope", "subnet_scan_list"]
|
||||
LOCAL_NETWORK_SCAN_PATH = ["basic_network", "scope", "local_network_scan"]
|
||||
LM_HASH_LIST_PATH = ["internal", "exploits", "exploit_lm_hash_list"]
|
||||
NTLM_HASH_LIST_PATH = ["internal", "exploits", "exploit_ntlm_hash_list"]
|
||||
|
||||
|
|
|
@ -3,21 +3,10 @@ from __future__ import annotations
|
|||
from bson import json_util
|
||||
|
||||
from monkey_island.cc.models.report.report import Report
|
||||
from monkey_island.cc.server_utils.encryption import (
|
||||
SensitiveField,
|
||||
StringListEncryptor,
|
||||
decrypt_dict,
|
||||
encrypt_dict,
|
||||
)
|
||||
|
||||
sensitive_fields = [
|
||||
SensitiveField(path="overview.config_passwords", field_encryptor=StringListEncryptor)
|
||||
]
|
||||
|
||||
|
||||
def save_report(report_dict: dict):
|
||||
report_dict = _encode_dot_char_before_mongo_insert(report_dict)
|
||||
report_dict = encrypt_dict(sensitive_fields, report_dict)
|
||||
Report.objects.delete()
|
||||
Report(
|
||||
overview=report_dict["overview"],
|
||||
|
@ -29,7 +18,7 @@ def save_report(report_dict: dict):
|
|||
|
||||
def get_report() -> dict:
|
||||
report_dict = Report.objects.first().to_mongo()
|
||||
return _decode_dot_char_before_mongo_insert(decrypt_dict(sensitive_fields, report_dict))
|
||||
return _decode_dot_char_before_mongo_insert(report_dict)
|
||||
|
||||
|
||||
# TODO remove this unnecessary encoding. I think these are legacy methods from back in the day
|
||||
|
|
|
@ -21,5 +21,4 @@ from .dict_encryptor import (
|
|||
FieldNotFoundError,
|
||||
)
|
||||
from .field_encryptors.i_field_encryptor import IFieldEncryptor
|
||||
from .field_encryptors.string_list_encryptor import StringListEncryptor
|
||||
from .field_encryptors.string_encryptor import StringEncryptor
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
from .i_field_encryptor import IFieldEncryptor
|
||||
from .string_list_encryptor import StringListEncryptor
|
||||
from .string_encryptor import StringEncryptor
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
from typing import List
|
||||
|
||||
from ..data_store_encryptor import get_datastore_encryptor
|
||||
from . import IFieldEncryptor
|
||||
|
||||
|
||||
class StringListEncryptor(IFieldEncryptor):
|
||||
@staticmethod
|
||||
def encrypt(value: List[str]):
|
||||
return [get_datastore_encryptor().encrypt(string.encode()) for string in value]
|
||||
|
||||
@staticmethod
|
||||
def decrypt(value: List[bytes]):
|
||||
return [get_datastore_encryptor().decrypt(bytes_).decode() for bytes_ in value]
|
|
@ -69,7 +69,11 @@ def initialize_services(data_dir: Path) -> DIContainer:
|
|||
_patch_credentials_parser(container)
|
||||
|
||||
# This is temporary until we get DI all worked out.
|
||||
ReportService.initialize(container.resolve(AWSService))
|
||||
ReportService.initialize(
|
||||
container.resolve(AWSService),
|
||||
container.resolve(IAgentConfigurationRepository),
|
||||
container.resolve(ICredentialsRepository),
|
||||
)
|
||||
|
||||
return container
|
||||
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
import functools
|
||||
import ipaddress
|
||||
import itertools
|
||||
import logging
|
||||
from itertools import chain, product
|
||||
from typing import List
|
||||
|
||||
from common.config_value_paths import (
|
||||
EXPLOITER_CLASSES_PATH,
|
||||
LOCAL_NETWORK_SCAN_PATH,
|
||||
PASSWORD_LIST_PATH,
|
||||
SUBNET_SCAN_LIST_PATH,
|
||||
USER_LIST_PATH,
|
||||
)
|
||||
from common.credentials import CredentialComponentType
|
||||
from common.network.network_range import NetworkRange
|
||||
from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst
|
||||
from monkey_island.cc.database import mongo
|
||||
from monkey_island.cc.models import Monkey
|
||||
from monkey_island.cc.models.report import get_report, save_report
|
||||
from monkey_island.cc.services.config import ConfigService
|
||||
from monkey_island.cc.repository import IAgentConfigurationRepository, ICredentialsRepository
|
||||
from monkey_island.cc.services.configuration.utils import (
|
||||
get_config_network_segments_as_subnet_groups,
|
||||
)
|
||||
|
@ -47,6 +41,8 @@ logger = logging.getLogger(__name__)
|
|||
class ReportService:
|
||||
|
||||
_aws_service = None
|
||||
_agent_configuration_repository = None
|
||||
_credentials_repository = None
|
||||
|
||||
class DerivedIssueEnum:
|
||||
WEAK_PASSWORD = "weak_password"
|
||||
|
@ -54,8 +50,15 @@ class ReportService:
|
|||
ZEROLOGON_PASS_RESTORE_FAILED = "zerologon_pass_restore_failed"
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, aws_service: AWSService):
|
||||
def initialize(
|
||||
cls,
|
||||
aws_service: AWSService,
|
||||
agent_configuration_repository: IAgentConfigurationRepository,
|
||||
credentials_repository: ICredentialsRepository,
|
||||
):
|
||||
cls._aws_service = aws_service
|
||||
cls._agent_configuration_repository = agent_configuration_repository
|
||||
cls._credentials_repository = credentials_repository
|
||||
|
||||
# This should pull from Simulation entity
|
||||
@staticmethod
|
||||
|
@ -305,7 +308,7 @@ class ReportService:
|
|||
"""
|
||||
cross_segment_issues = []
|
||||
|
||||
for subnet_pair in itertools.product(subnet_group, subnet_group):
|
||||
for subnet_pair in product(subnet_group, subnet_group):
|
||||
source_subnet = subnet_pair[0]
|
||||
target_subnet = subnet_pair[1]
|
||||
pair_issues = ReportService.get_cross_segment_issues_per_subnet_pair(
|
||||
|
@ -381,37 +384,60 @@ class ReportService:
|
|||
def get_manual_monkey_hostnames():
|
||||
return [monkey["hostname"] for monkey in get_manual_monkeys()]
|
||||
|
||||
@staticmethod
|
||||
def get_config_users():
|
||||
return ConfigService.get_config_value(USER_LIST_PATH, True)
|
||||
@classmethod
|
||||
def get_config_users(cls):
|
||||
usernames = []
|
||||
configured_credentials = cls._credentials_repository.get_configured_credentials()
|
||||
for credentials in configured_credentials:
|
||||
usernames = chain(
|
||||
usernames,
|
||||
(
|
||||
identity
|
||||
for identity in credentials.identities
|
||||
if identity.credential_type == CredentialComponentType.USERNAME
|
||||
),
|
||||
)
|
||||
return [u.username for u in usernames]
|
||||
|
||||
@staticmethod
|
||||
def get_config_passwords():
|
||||
return ConfigService.get_config_value(PASSWORD_LIST_PATH, True)
|
||||
@classmethod
|
||||
def get_config_passwords(cls):
|
||||
passwords = []
|
||||
configured_credentials = cls._credentials_repository.get_configured_credentials()
|
||||
for credentials in configured_credentials:
|
||||
passwords = chain(
|
||||
passwords,
|
||||
(
|
||||
secret
|
||||
for secret in credentials.secrets
|
||||
if secret.credential_type == CredentialComponentType.PASSWORD
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_config_exploits():
|
||||
exploits_config_value = EXPLOITER_CLASSES_PATH
|
||||
# TODO: Return default config here
|
||||
default_exploits = ConfigService.get_default_config(False)
|
||||
for namespace in exploits_config_value:
|
||||
default_exploits = default_exploits[namespace]
|
||||
exploits = ConfigService.get_config_value(exploits_config_value, True)
|
||||
return [p.password for p in passwords]
|
||||
|
||||
if exploits == default_exploits:
|
||||
return ["default"]
|
||||
@classmethod
|
||||
def get_config_exploits(cls):
|
||||
agent_configuration = cls._agent_configuration_repository.get_configuration()
|
||||
exploitation_configuration = agent_configuration.propagation.exploitation
|
||||
|
||||
enabled_exploiters = chain(
|
||||
exploitation_configuration.brute_force, exploitation_configuration.vulnerability
|
||||
)
|
||||
|
||||
return [
|
||||
ExploiterDescriptorEnum.get_by_class_name(exploit).display_name for exploit in exploits
|
||||
ExploiterDescriptorEnum.get_by_class_name(exploiter.name).display_name
|
||||
for exploiter in enabled_exploiters
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_config_ips():
|
||||
return ConfigService.get_config_value(SUBNET_SCAN_LIST_PATH, True)
|
||||
@classmethod
|
||||
def get_config_ips(cls):
|
||||
agent_configuration = cls._agent_configuration_repository.get_configuration()
|
||||
return agent_configuration.propagation.network_scan.targets.subnets
|
||||
|
||||
@staticmethod
|
||||
def get_config_scan():
|
||||
return ConfigService.get_config_value(LOCAL_NETWORK_SCAN_PATH, True)
|
||||
@classmethod
|
||||
def get_config_scan(cls):
|
||||
agent_configuration = cls._agent_configuration_repository.get_configuration()
|
||||
return agent_configuration.propagation.network_scan.targets.local_network_scan
|
||||
|
||||
@staticmethod
|
||||
def get_issue_set(issues, config_users, config_passwords):
|
||||
|
@ -478,8 +504,6 @@ class ReportService:
|
|||
report = {
|
||||
"overview": {
|
||||
"manual_monkeys": ReportService.get_manual_monkey_hostnames(),
|
||||
"config_users": config_users,
|
||||
"config_passwords": config_passwords,
|
||||
"config_exploits": ReportService.get_config_exploits(),
|
||||
"config_ips": ReportService.get_config_ips(),
|
||||
"config_scan": ReportService.get_config_scan(),
|
||||
|
|
|
@ -297,17 +297,12 @@ class ReportPageComponent extends AuthComponent {
|
|||
}
|
||||
{
|
||||
this.state.report.overview.config_exploits.length > 0 ?
|
||||
(
|
||||
this.state.report.overview.config_exploits[0] === 'default' ?
|
||||
''
|
||||
:
|
||||
<p>
|
||||
The Monkey uses the following exploit methods:
|
||||
<ul>
|
||||
{this.state.report.overview.config_exploits.map(x => <li key={x}>{x}</li>)}
|
||||
</ul>
|
||||
</p>
|
||||
)
|
||||
<p>
|
||||
The Monkey uses the following exploit methods:
|
||||
<ul>
|
||||
{this.state.report.overview.config_exploits.map(x => <li key={x}>{x}</li>)}
|
||||
</ul>
|
||||
</p>
|
||||
:
|
||||
<p>
|
||||
No exploits are used by the Monkey.
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
import copy
|
||||
from typing import List
|
||||
|
||||
import pytest
|
||||
|
||||
from monkey_island.cc.models import Report
|
||||
from monkey_island.cc.models.report import get_report, save_report
|
||||
from monkey_island.cc.server_utils.encryption import IFieldEncryptor, SensitiveField
|
||||
|
||||
MOCK_SENSITIVE_FIELD_CONTENTS = ["the_string", "the_string2"]
|
||||
MOCK_REPORT_DICT = {
|
||||
"overview": {
|
||||
"foo": {"the_key": MOCK_SENSITIVE_FIELD_CONTENTS, "other_key": "other_value"},
|
||||
"foo": {"the_key": ["the_string", "the_string2"], "other_key": "other_value"},
|
||||
"bar": {"the_key": []},
|
||||
},
|
||||
"glance": {"foo": "bar"},
|
||||
|
@ -19,43 +17,6 @@ MOCK_REPORT_DICT = {
|
|||
}
|
||||
|
||||
|
||||
class MockStringListEncryptor(IFieldEncryptor):
|
||||
plaintext = []
|
||||
|
||||
@staticmethod
|
||||
def encrypt(value: List[str]) -> List[str]:
|
||||
return [MockStringListEncryptor._encrypt(v) for v in value]
|
||||
|
||||
@staticmethod
|
||||
def _encrypt(value: str) -> str:
|
||||
MockStringListEncryptor.plaintext.append(value)
|
||||
return f"ENCRYPTED_{str(len(MockStringListEncryptor.plaintext) - 1)}"
|
||||
|
||||
@staticmethod
|
||||
def decrypt(value: List[str]) -> List[str]:
|
||||
return MockStringListEncryptor.plaintext
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def patch_sensitive_fields(monkeypatch):
|
||||
mock_sensitive_fields = [
|
||||
SensitiveField("overview.foo.the_key", MockStringListEncryptor),
|
||||
SensitiveField("overview.bar.the_key", MockStringListEncryptor),
|
||||
]
|
||||
monkeypatch.setattr(
|
||||
"monkey_island.cc.models.report.report_dal.sensitive_fields", mock_sensitive_fields
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("uses_database")
|
||||
def test_report_encryption():
|
||||
save_report(MOCK_REPORT_DICT)
|
||||
|
||||
assert Report.objects.first()["overview"]["foo"]["the_key"] == ["ENCRYPTED_0", "ENCRYPTED_1"]
|
||||
assert Report.objects.first()["overview"]["bar"]["the_key"] == []
|
||||
assert get_report()["overview"]["foo"]["the_key"] == MOCK_SENSITIVE_FIELD_CONTENTS
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("uses_database")
|
||||
def test_report_dot_encoding():
|
||||
mrd = copy.deepcopy(MOCK_REPORT_DICT)
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import pytest
|
||||
|
||||
from monkey_island.cc.server_utils.encryption import StringListEncryptor
|
||||
|
||||
MOCK_STRING_LIST = ["test_1", "test_2"]
|
||||
EMPTY_LIST = []
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_encryption_and_decryption(uses_encryptor):
|
||||
encrypted_list = StringListEncryptor.encrypt(MOCK_STRING_LIST)
|
||||
assert not encrypted_list == MOCK_STRING_LIST
|
||||
decrypted_list = StringListEncryptor.decrypt(encrypted_list)
|
||||
assert decrypted_list == MOCK_STRING_LIST
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_empty_list(uses_encryptor):
|
||||
# Tests that no errors are raised
|
||||
encrypted_list = StringListEncryptor.encrypt(EMPTY_LIST)
|
||||
StringListEncryptor.decrypt(encrypted_list)
|
Loading…
Reference in New Issue