diff --git a/monkey/monkey_island/cc/models/__init__.py b/monkey/monkey_island/cc/models/__init__.py index 5e4d1cef7..5ed812b7d 100644 --- a/monkey/monkey_island/cc/models/__init__.py +++ b/monkey/monkey_island/cc/models/__init__.py @@ -7,6 +7,5 @@ from .monkey import Monkey from .monkey_ttl import MonkeyTtl from .pba_results import PbaResults from monkey_island.cc.models.report.report import Report -from .stolen_credentials import StolenCredentials from .simulation import Simulation, SimulationSchema, IslandMode from .user_credentials import UserCredentials diff --git a/monkey/monkey_island/cc/models/stolen_credentials.py b/monkey/monkey_island/cc/models/stolen_credentials.py deleted file mode 100644 index 968610165..000000000 --- a/monkey/monkey_island/cc/models/stolen_credentials.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -from mongoengine import Document, ListField, ReferenceField - -from monkey_island.cc.models import Monkey - - -class StolenCredentials(Document): - """ - This class has 2 main section: - * The schema section defines the DB fields in the document. This is the - data of the object. - * The logic section defines complex questions we can ask about a single document - which are asked multiple times, somewhat like an API. - - """ - - # SCHEMA - monkey = ReferenceField(Monkey) - identities = ListField() - secrets = ListField() diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py index 81cd7ad69..cb29f7c49 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -1,7 +1,7 @@ from common.utils.attack_utils import ScanStatus -from monkey_island.cc.models import StolenCredentials +from monkey_island.cc.repository import ICredentialsRepository from monkey_island.cc.services.attack.technique_reports import AttackTechnique -from monkey_island.cc.services.reporting.stolen_credentials import get_stolen_creds +from monkey_island.cc.services.reporting import format_creds_for_reporting class T1003(AttackTechnique): @@ -16,8 +16,21 @@ class T1003(AttackTechnique): @staticmethod def get_report_data(): + raise NotImplementedError + + +class T1003GetReportData: + """ + Class to patch the T1003 attack technique which + needs stolen credentials from db. + """ + + def __init__(self, credentials_repository: ICredentialsRepository): + self._credentials_repository = credentials_repository + + def __call__(self): def get_technique_status_and_data(): - if list(StolenCredentials.objects()): + if list(self._credentials_repository.get_stolen_credentials()): status = ScanStatus.USED.value else: status = ScanStatus.UNSCANNED.value @@ -28,5 +41,7 @@ class T1003(AttackTechnique): data.update(T1003.get_message_and_status(status)) data.update(T1003.get_mitigation_by_status(status)) - data["stolen_creds"] = get_stolen_creds() + data["stolen_creds"] = format_creds_for_reporting( + self._credentials_repository.get_stolen_credentials() + ) return data diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index 26209ce6c..260fa168e 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -33,6 +33,7 @@ from monkey_island.cc.repository import ( from monkey_island.cc.server_utils.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.server_utils.encryption import ILockableEncryptor, RepositoryEncryptor from monkey_island.cc.services import AWSService, IslandModeService, RepositoryService +from monkey_island.cc.services.attack.technique_reports.T1003 import T1003, T1003GetReportData from monkey_island.cc.services.run_local_monkey import LocalMonkeyRunService from monkey_island.cc.services.telemetry.processing.credentials.credentials_parser import ( CredentialsParser, @@ -64,9 +65,7 @@ def initialize_services(data_dir: Path) -> DIContainer: _register_repositories(container, data_dir) _register_services(container) - # Note: A hack to resolve credentials parser - # It changes telemetry processing function, this will be refactored! - _patch_credentials_parser(container) + _dirty_hacks(container) # This is temporary until we get DI all worked out. ReportService.initialize( @@ -153,7 +152,16 @@ def _register_services(container: DIContainer): container.register_instance(RepositoryService, container.resolve(RepositoryService)) -def _patch_credentials_parser(container: DIContainer): +def _dirty_hacks(container: DIContainer): + # A dirty hacks function that patches some of the things that + # are needed at the current point + + # Patches attack technique T1003 which is a static class + # but it needs stolen credentials from the database + T1003.get_report_data = container.resolve(T1003GetReportData) + + # Note: A hack to resolve credentials parser + # It changes telemetry processing function, this will be refactored! TELEMETRY_CATEGORY_TO_PROCESSING_FUNC[TelemCategoryEnum.CREDENTIALS] = container.resolve( CredentialsParser ) diff --git a/monkey/monkey_island/cc/services/reporting/__init__.py b/monkey/monkey_island/cc/services/reporting/__init__.py index e69de29bb..75facb01b 100644 --- a/monkey/monkey_island/cc/services/reporting/__init__.py +++ b/monkey/monkey_island/cc/services/reporting/__init__.py @@ -0,0 +1 @@ +from .format_credentials import format_creds_for_reporting diff --git a/monkey/monkey_island/cc/services/reporting/format_credentials.py b/monkey/monkey_island/cc/services/reporting/format_credentials.py new file mode 100644 index 000000000..721868cdc --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/format_credentials.py @@ -0,0 +1,38 @@ +import logging +from typing import Mapping, Sequence + +from common.credentials import CredentialComponentType, Credentials + +logger = logging.getLogger(__name__) + + +def format_creds_for_reporting(credentials: Sequence[Credentials]) -> Sequence[Mapping]: + logger.info("Stolen creds generated for reporting") + + formatted_creds = [] + cred_type_dict = { + CredentialComponentType.PASSWORD: "Clear Password", + CredentialComponentType.LM_HASH: "LM hash", + CredentialComponentType.NT_HASH: "NTLM hash", + CredentialComponentType.SSH_KEYPAIR: "Clear SSH private key", + } + for cred in credentials: + secret = cred.secret + if secret is None: + continue + + if secret.credential_type not in cred_type_dict: + continue + username = _get_username(cred) + cred_row = { + "username": username, + "_type": secret.credential_type.name, + "type": cred_type_dict[secret.credential_type], + } + if cred_row not in formatted_creds: + formatted_creds.append(cred_row) + return formatted_creds + + +def _get_username(credentials: Credentials) -> str: + return credentials.identity.username if credentials.identity else "" diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index dd19f8750..1a46a6566 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -4,7 +4,6 @@ import logging from itertools import chain, product from typing import List -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 @@ -20,16 +19,11 @@ from monkey_island.cc.services.reporting.pth_report import PTHReportService from monkey_island.cc.services.reporting.report_generation_synchronisation import ( safe_generate_regular_report, ) -from monkey_island.cc.services.reporting.stolen_credentials import ( - extract_ssh_keys, - get_stolen_creds, -) from monkey_island.cc.services.utils.network_utils import get_subnets, local_ip_addresses from .. import AWSService from . import aws_exporter from .issue_processing.exploit_processing.exploiter_descriptor_enum import ExploiterDescriptorEnum -from .issue_processing.exploit_processing.processors.cred_exploit import CredentialType from .issue_processing.exploit_processing.processors.exploit import ExploiterReportInfo logger = logging.getLogger(__name__) @@ -42,8 +36,6 @@ class ReportService: _credentials_repository = None class DerivedIssueEnum: - WEAK_PASSWORD = "weak_password" - STOLEN_CREDS = "stolen_creds" ZEROLOGON_PASS_RESTORE_FAILED = "zerologon_pass_restore_failed" @classmethod @@ -382,37 +374,6 @@ class ReportService: def get_manual_monkey_hostnames(): return [monkey["hostname"] for monkey in get_manual_monkeys()] - @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] - - @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 - ), - ) - - return [p.password for p in passwords] - @classmethod def get_config_exploits(cls): agent_configuration = cls._agent_configuration_repository.get_configuration() @@ -438,42 +399,18 @@ class ReportService: return agent_configuration.propagation.network_scan.targets.local_network_scan @staticmethod - def get_issue_set(issues, config_users, config_passwords): + def get_issue_set(issues): issue_set = set() for machine in issues: for issue in issues[machine]: - if ReportService._is_weak_credential_issue(issue, config_users, config_passwords): - issue_set.add(ReportService.DerivedIssueEnum.WEAK_PASSWORD) - elif ReportService._is_stolen_credential_issue(issue): - issue_set.add(ReportService.DerivedIssueEnum.STOLEN_CREDS) - elif ReportService._is_zerologon_pass_restore_failed(issue): + if ReportService._is_zerologon_pass_restore_failed(issue): issue_set.add(ReportService.DerivedIssueEnum.ZEROLOGON_PASS_RESTORE_FAILED) issue_set.add(issue["type"]) return issue_set - @staticmethod - def _is_weak_credential_issue( - issue: dict, config_usernames: List[str], config_passwords: List[str] - ) -> bool: - # Only credential exploiter issues have 'credential_type' - return ( - "credential_type" in issue - and issue["credential_type"] == CredentialType.PASSWORD.value - and issue["password"] in config_passwords - and issue["username"] in config_usernames - ) - - @staticmethod - def _is_stolen_credential_issue(issue: dict) -> bool: - # Only credential exploiter issues have 'credential_type' - return "credential_type" in issue and ( - issue["credential_type"] == CredentialType.PASSWORD.value - or issue["credential_type"] == CredentialType.HASH.value - ) - @staticmethod def _is_zerologon_pass_restore_failed(issue: dict): return ( @@ -490,12 +427,9 @@ class ReportService: def generate_report(): domain_issues = ReportService.get_domain_issues() issues = ReportService.get_issues() - config_users = ReportService.get_config_users() - config_passwords = ReportService.get_config_passwords() - issue_set = ReportService.get_issue_set(issues, config_users, config_passwords) + issue_set = ReportService.get_issue_set(issues) cross_segment_issues = ReportService.get_cross_segment_issues() monkey_latest_modify_time = Monkey.get_latest_modifytime() - stolen_creds = get_stolen_creds() scanned_nodes = ReportService.get_scanned() exploited_cnt = len(get_monkey_exploited()) @@ -515,8 +449,6 @@ class ReportService: "glance": { "scanned": scanned_nodes, "exploited_cnt": exploited_cnt, - "stolen_creds": stolen_creds, - "ssh_keys": extract_ssh_keys(stolen_creds), "strong_users": PTHReportService.get_strong_users_on_crit_details(), }, "recommendations": {"issues": issues, "domain_issues": domain_issues}, diff --git a/monkey/monkey_island/cc/services/reporting/stolen_credentials.py b/monkey/monkey_island/cc/services/reporting/stolen_credentials.py deleted file mode 100644 index 2a817ac75..000000000 --- a/monkey/monkey_island/cc/services/reporting/stolen_credentials.py +++ /dev/null @@ -1,52 +0,0 @@ -import logging -from typing import Mapping, Sequence - -from common.credentials import CredentialComponentType -from monkey_island.cc.models import StolenCredentials - -logger = logging.getLogger(__name__) - - -def get_stolen_creds() -> Sequence[Mapping]: - stolen_creds = _fetch_from_db() - stolen_creds = _format_creds_for_reporting(stolen_creds) - - logger.info("Stolen creds generated for reporting") - return stolen_creds - - -def extract_ssh_keys(credentials: Sequence[Mapping]) -> Sequence[Mapping]: - return [c for c in credentials if c["_type"] == CredentialComponentType.SSH_KEYPAIR.name] - - -def _fetch_from_db() -> Sequence[StolenCredentials]: - return list(StolenCredentials.objects()) - - -def _format_creds_for_reporting(credentials: Sequence[StolenCredentials]): - formatted_creds = [] - cred_type_dict = { - CredentialComponentType.PASSWORD.name: "Clear Password", - CredentialComponentType.LM_HASH.name: "LM hash", - CredentialComponentType.NT_HASH.name: "NTLM hash", - CredentialComponentType.SSH_KEYPAIR.name: "Clear SSH private key", - } - - for cred in credentials: - for secret_type in cred.secrets: - if secret_type not in cred_type_dict: - continue - username = _get_username(cred) - cred_row = { - "username": username, - "_type": secret_type, - "type": cred_type_dict[secret_type], - "origin": cred.monkey.hostname, - } - if cred_row not in formatted_creds: - formatted_creds.append(cred_row) - return formatted_creds - - -def _get_username(credentials: StolenCredentials) -> str: - return credentials.identities[0]["username"] if credentials.identities else "" diff --git a/monkey/monkey_island/cc/services/telemetry/processing/credentials/credentials_parser.py b/monkey/monkey_island/cc/services/telemetry/processing/credentials/credentials_parser.py index be14047be..4d675b42c 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/credentials/credentials_parser.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/credentials/credentials_parser.py @@ -15,10 +15,10 @@ class CredentialsParser: def __init__(self, credentials_repository: ICredentialsRepository): self._credentials_repository = credentials_repository - def __call__(self, telemetry_dict): - self._parse_credentials(telemetry_dict) + def __call__(self, telemetry_dict, _agent_configuration): + self._parse_credentials(telemetry_dict, _agent_configuration) - def _parse_credentials(self, telemetry_dict: Mapping): + def _parse_credentials(self, telemetry_dict: Mapping, _agent_configuration): credentials = [ Credentials.from_mapping(credential) for credential in telemetry_dict["data"] ] diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js index 1a93c8e06..5d5cd72c8 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js @@ -18,7 +18,9 @@ class T1003 extends React.Component {
{this.props.data.status === ScanStatus.USED ? + data={this.props.data.stolen_creds} + format={false} + /> : ''} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js index d91d2d0fc..fd1accfb8 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js @@ -37,7 +37,6 @@ import { } from './security/issues/SharedPasswordsIssue'; import {tunnelIssueReport, tunnelIssueOverview} from './security/issues/TunnelIssue'; import {stolenCredsIssueOverview} from './security/issues/StolenCredsIssue'; -import {weakPasswordIssueOverview} from './security/issues/WeakPasswordIssue'; import {strongUsersOnCritIssueReport} from './security/issues/StrongUsersOnCritIssue'; import { zerologonIssueOverview, @@ -45,6 +44,7 @@ import { zerologonOverviewWithFailedPassResetWarning } from './security/issues/ZerologonIssue'; import {powershellIssueOverview, powershellIssueReport} from './security/issues/PowershellIssue'; +import UsedCredentials from './security/UsedCredentials'; class ReportPageComponent extends AuthComponent { @@ -146,10 +146,8 @@ class ReportPageComponent extends AuthComponent { [this.issueContentTypes.REPORT]: strongUsersOnCritIssueReport, [this.issueContentTypes.TYPE]: this.issueTypes.DANGER }, - 'weak_password': { - [this.issueContentTypes.OVERVIEW]: weakPasswordIssueOverview, - [this.issueContentTypes.TYPE]: this.issueTypes.DANGER - }, + // TODO: Add used_password issue: configured password that were + // successfull exploiting a machine, previously called 'weak_password' 'stolen_creds': { [this.issueContentTypes.OVERVIEW]: stolenCredsIssueOverview, [this.issueContentTypes.TYPE]: this.issueTypes.DANGER @@ -161,15 +159,32 @@ class ReportPageComponent extends AuthComponent { this.state = { report: props.report, graph: {nodes: [], edges: []}, - nodeStateList: [] + nodeStateList: [], + stolenCredentials: [], + configuredCredentials: [], + issues: [] }; } componentDidMount() { this.getNodeStateListFromServer(); + this.getCredentialsFromServer(); this.updateMapFromServer(); } + getCredentialsFromServer = () => { + this.authFetch('/api/propagation-credentials/stolen-credentials') + .then(res => res.json()) + .then(creds => { + this.setState({stolenCredentials: creds}); + }) + this.authFetch('/api/propagation-credentials/configured-credentials') + .then(res => res.json()) + .then(creds => { + this.setState({configuredCredentials: creds}); + }) + } + getNodeStateListFromServer = () => { this.authFetch('/api/netmap/node-states') .then(res => res.json()) @@ -184,7 +199,8 @@ class ReportPageComponent extends AuthComponent { componentDidUpdate(prevProps) { if (this.props.report !== prevProps.report) { - this.setState({report: this.props.report}) + this.setState({report: this.props.report}); + this.addIssuesToOverviewIssues(); } } @@ -273,39 +289,20 @@ class ReportPageComponent extends AuthComponent {

The monkeys were run with the following configuration:

- { - this.state.report.overview.config_users.length > 0 ? - <> -

- Usernames used for brute-forcing: -

- -

- Passwords used for brute-forcing: -

- - - : -

- Brute forcing uses stolen credentials only. No credentials were supplied during Monkey’s - configuration. -

- } + { this.state.report.overview.config_exploits.length > 0 ? -

- The Monkey uses the following exploit methods: -

    - {this.state.report.overview.config_exploits.map(x =>
  • {x}
  • )} -
-

+ ( +

+ The Monkey attempted the following exploitation methods: +

    + {this.state.report.overview.config_exploits.map(x =>
  • {x}
  • )} +
+

+ ) :

- No exploits are used by the Monkey. + No exploiters were enabled.

} { @@ -379,6 +376,9 @@ class ReportPageComponent extends AuthComponent { getPotentialSecurityIssuesOverviews() { let overviews = []; let issues = this.state.report.overview.issues; + let state_issues = this.state.issues; + issues.push(...state_issues); + issues = [...new Set(issues)]; for(let i=0; i < issues.length; i++) { if (this.isIssuePotentialSecurityIssue(issues[i])) { @@ -420,6 +420,10 @@ class ReportPageComponent extends AuthComponent { getImmediateThreatCount() { let threatCount = 0; let issues = this.state.report.overview.issues; + let state_issues = this.state.issues; + + issues.push(...state_issues); + issues = [...new Set(issues)]; for(let i=0; i < issues.length; i++) { if(this.isIssueImmediateThreat(issues[i])) { @@ -439,6 +443,10 @@ class ReportPageComponent extends AuthComponent { getImmediateThreatsOverviews() { let overviews = []; let issues = this.state.report.overview.issues; + let state_issues = this.state.issues; + + issues.push(...state_issues); + issues = [...new Set(issues)]; for(let i=0; i < issues.length; i++) { if (this.isIssueImmediateThreat(issues[i])) { @@ -535,7 +543,10 @@ class ReportPageComponent extends AuthComponent {
- +
@@ -582,6 +593,23 @@ class ReportPageComponent extends AuthComponent { } return
    {issuesDivArray}
; }; + + addIssuesToOverviewIssues() { + let overview_issues = this.state.issues; + + if (this.shouldAddStolenCredentialsIssue()) { + overview_issues.push('stolen_creds'); + } + this.setState({ + issues: overview_issues + }); + } + + shouldAddStolenCredentialsIssue() { + // TODO: This should check if any stolen credentials are used to + // exploit a machine + return ( this.state.stolenCredentials.length > 0 ) + } } export default ReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js b/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js new file mode 100644 index 000000000..ea95a88d8 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js @@ -0,0 +1,57 @@ +export function getAllUsernames(stolen, configured){ + let usernames = []; + usernames.push(...getCredentialsUsernames(stolen)); + usernames.push(...getCredentialsUsernames(configured)); + return usernames; +} + +export function getCredentialsUsernames(credentials) { + let usernames = []; + for(let i = 0; i < credentials.length; i++){ + usernames.push(credentials[i]['identity']['username']); + } + return usernames; +} + +export function getAllSecrets(stolen, configured){ + let secrets = []; + for(let i = 0; i < stolen.length; i++){ + secrets.push(getSecretsFromCredential(stolen[i]['secret'])); + } + for(let i = 0; i < configured.length; i++){ + secrets.push(getSecretsFromCredential(configured[i]['secret'])); + } + return secrets; +} + +function getSecretsFromCredential(credential) { + if(credential['credential_type'] === 'SSH_KEYPAIR'){ + return {'type': 'SSH keypair', 'content': credential['private_key']} + } + if(credential['credential_type'] === 'NT_HASH'){ + return {'type': 'NT hash', 'content': credential['nt_hash']} + } + if(credential['credential_type'] === 'LM_HASH'){ + return {'type': 'LM hash', 'content': credential['lm_hash']} + } + if(credential['credential_type'] === 'PASSWORD'){ + return {'type': 'Password', 'content': credential['password']} + } +} + +export function getCredentialsTableData(credentials) { + + let table_data = []; + + let identites = getCredentialsUsernames(credentials); + let secrets = getAllSecrets(credentials, []) + + for(let i=0; i pageSize ? pageSize : this.props.data.length; let showPagination = this.props.data.length > pageSize; + let table_data = this.props.data; + if(this.props.format) { + // Note: This formatting is needed because StolenPasswords + // is used in Security and Attack report with different data + table_data = getCredentialsTableData(this.props.data); + } + return (
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.js new file mode 100644 index 000000000..7a5becde7 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.js @@ -0,0 +1,40 @@ +import {getAllUsernames, getAllSecrets} from '../credentialParsing'; +import React from 'react'; + +class UsedCredentials extends React.Component { + constructor(props) { + super(props); + } + + + render() { + let allUsernames = getAllUsernames(this.props.stolen, this.props.configured); + let allSecrets = getAllSecrets(this.props.stolen, this.props.configured); + return ( + allUsernames.length > 0 ? + <> +

+ Usernames used for brute-forcing: +

+
    + {allUsernames.map(x =>
  • {x}
  • )} +
+

+ Credentials used for brute-forcing: +

+
    + {allSecrets.map((x, index) =>
  • {x['type']}: {x['content'].substr(0, 3) + '******'}
  • )} +
+ + : +

+ No credentials were used. +

+ ) + + } + +} + +export default UsedCredentials; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WeakPasswordIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WeakPasswordIssue.js deleted file mode 100644 index ee3c6c04f..000000000 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WeakPasswordIssue.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; - -export function weakPasswordIssueOverview() { - return (
  • Machines are accessible using passwords supplied by the user during the Monkey’s - configuration.
  • ) -} diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_format_credentials.py b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_format_credentials.py new file mode 100644 index 000000000..bb51b89dd --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_format_credentials.py @@ -0,0 +1,69 @@ +from common.credentials import ( + CredentialComponentType, + Credentials, + LMHash, + NTHash, + Password, + SSHKeypair, + Username, +) +from monkey_island.cc.services.reporting import format_creds_for_reporting + +monkey_hostname = "fake_hostname" +fake_monkey_guid = "abc" + +fake_username = Username("m0nk3y_user") +fake_nt_hash = NTHash("AEBD4DE384C7EC43AAD3B435B51404EE") +fake_lm_hash = LMHash("7A21990FCD3D759941E45C490F143D5F") +fake_password = Password("trytostealthis") +fake_ssh_public_key = "RSA_public_key" +fake_ssh_private_key = "RSA_private_key" +fake_ssh_key = SSHKeypair(fake_ssh_private_key, fake_ssh_public_key) + +identities = (fake_username,) +secrets = (fake_nt_hash, fake_lm_hash, fake_password, fake_ssh_key) + +fake_credentials = [ + Credentials(fake_username, fake_nt_hash), + Credentials(fake_username, fake_lm_hash), + Credentials(fake_username, fake_password), + Credentials(fake_username, fake_ssh_key), + Credentials(None, fake_ssh_key), + Credentials(fake_username, None), +] + + +def test_formatting_credentials_for_report(): + + credentials = format_creds_for_reporting(fake_credentials) + + result1 = { + "_type": CredentialComponentType.NT_HASH.name, + "type": "NTLM hash", + "username": fake_username.username, + } + result2 = { + "_type": CredentialComponentType.LM_HASH.name, + "type": "LM hash", + "username": fake_username.username, + } + result3 = { + "_type": CredentialComponentType.PASSWORD.name, + "type": "Clear Password", + "username": fake_username.username, + } + result4 = { + "_type": CredentialComponentType.SSH_KEYPAIR.name, + "type": "Clear SSH private key", + "username": fake_username.username, + } + result5 = { + "_type": CredentialComponentType.SSH_KEYPAIR.name, + "type": "Clear SSH private key", + "username": "", + } + assert result1 in credentials + assert result2 in credentials + assert result3 in credentials + assert result4 in credentials + assert result5 in credentials diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_stolen_credentials.py b/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_stolen_credentials.py deleted file mode 100644 index d7c2386c9..000000000 --- a/monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_stolen_credentials.py +++ /dev/null @@ -1,97 +0,0 @@ -import pytest - -from common.credentials import CredentialComponentType -from monkey_island.cc.models import Monkey, StolenCredentials -from monkey_island.cc.services.reporting.stolen_credentials import ( - extract_ssh_keys, - get_stolen_creds, -) - -monkey_hostname = "fake_hostname" -fake_monkey_guid = "abc" - -fake_username = "m0nk3y_user" -fake_nt_hash = "c1c58f96cdf212b50837bc11a00be47c" -fake_lm_hash = "299BD128C1101FD6" -fake_password = "trytostealthis" -fake_ssh_key = "RSA_fake_key" -fake_credentials = { - "identities": [{"username": fake_username, "credential_type": "USERNAME"}], - "secrets": [ - CredentialComponentType.NT_HASH.name, - CredentialComponentType.LM_HASH.name, - CredentialComponentType.PASSWORD.name, - CredentialComponentType.SSH_KEYPAIR.name, - ], -} - - -@pytest.fixture -def fake_monkey(): - monkey = Monkey() - monkey.guid = fake_monkey_guid - monkey.hostname = monkey_hostname - monkey.save() - return monkey.id - - -@pytest.mark.usefixtures("uses_database") -def test_get_credentials(fake_monkey): - StolenCredentials( - identities=fake_credentials["identities"], - secrets=fake_credentials["secrets"], - monkey=fake_monkey, - ).save() - - credentials = get_stolen_creds() - - result1 = { - "origin": monkey_hostname, - "_type": CredentialComponentType.NT_HASH.name, - "type": "NTLM hash", - "username": fake_username, - } - result2 = { - "origin": monkey_hostname, - "_type": CredentialComponentType.LM_HASH.name, - "type": "LM hash", - "username": fake_username, - } - result3 = { - "origin": monkey_hostname, - "_type": CredentialComponentType.PASSWORD.name, - "type": "Clear Password", - "username": fake_username, - } - result4 = { - "origin": monkey_hostname, - "_type": CredentialComponentType.SSH_KEYPAIR.name, - "type": "Clear SSH private key", - "username": fake_username, - } - assert result1 in credentials - assert result2 in credentials - assert result3 in credentials - assert result4 in credentials - - -@pytest.mark.usefixtures("uses_database") -def test_extract_ssh_keys(fake_monkey): - StolenCredentials( - identities=fake_credentials["identities"], - secrets=fake_credentials["secrets"], - monkey=fake_monkey, - ).save() - - credentials = get_stolen_creds() - keys = extract_ssh_keys(credentials) - - assert len(keys) == 1 - - result = { - "origin": monkey_hostname, - "_type": CredentialComponentType.SSH_KEYPAIR.name, - "type": "Clear SSH private key", - "username": fake_username, - } - assert result in keys