Merge pull request #2088 from guardicore/1662-remove-configservice-from-reportservice

1662 remove configservice from reportservice
This commit is contained in:
Mike Salvatore 2022-07-14 07:51:04 -04:00 committed by GitHub
commit 753970f644
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 73 additions and 140 deletions

View File

@ -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"]

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,2 @@
from .i_field_encryptor import IFieldEncryptor
from .string_list_encryptor import StringListEncryptor
from .string_encryptor import StringEncryptor

View File

@ -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]

View File

@ -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

View File

@ -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(),

View File

@ -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.

View File

@ -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)

View File

@ -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)