From f0f4f6d591418475964a3ea1c7862cb54adffc95 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 14 Jul 2022 16:47:33 +0200 Subject: [PATCH 01/29] Island: Remove credentials from reporting --- .../cc/services/reporting/report.py | 44 ++----------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index dd19f8750..a6d45e3e2 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -20,16 +20,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 +37,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 @@ -438,42 +431,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 +459,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 +481,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}, @@ -532,8 +496,6 @@ class ReportService: ReportService.get_exploits, ReportService.get_tunnels, ReportService.get_island_cross_segment_issues, - PTHReportService.get_duplicated_passwords_issues, - PTHReportService.get_strong_users_on_crit_issues, ] issues = functools.reduce(lambda acc, issue_gen: acc + issue_gen(), ISSUE_GENERATORS, []) From e61b0bfdcad98ae24920f4cc2ad0af3f5236663c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 14 Jul 2022 16:48:27 +0200 Subject: [PATCH 02/29] UI: Add credentials parsing functionalities --- .../report-components/credentialParsing.js | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js 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..73ece9502 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js @@ -0,0 +1,66 @@ +export function getAllUsernames(stolen, configured){ + let usernames = []; + usernames.push(...getCredentialsUsernames(stolen)); + usernames.push(...getCredentialsUsernames(configured)); + return usernames; +} + +export function getCredentialsSecrets(credentials, credential_type) { + let secrets = []; + + for(let i = 0; i < credentials.length; i++){ + secrets.push(credentials[i]['secrets'][0][credential_type]); + } + return secrets; +} + +export function getCredentialsUsernames(credentials) { + let usernames = []; + for(let i = 0; i < credentials.length; i++){ + usernames.push(credentials[i]['identities'][0]['username']); + } + return usernames; +} + +export function getAllSecrets(stolen, configured){ + let secrets = []; + for(let i = 0; i < stolen.length; i++){ + secrets.push(getSecretsFromCredential(stolen[i]['secrets'][0])); + } + for(let i = 0; i < configured.length; i++){ + secrets.push(getSecretsFromCredential(configured[i]['secrets'][0])); + } + 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 Date: Thu, 14 Jul 2022 16:48:58 +0200 Subject: [PATCH 03/29] UI: Add UsedCredentials component to render credentials --- .../security/UsedCredentials.tsx.js | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js new file mode 100644 index 000000000..b255a7ca9 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js @@ -0,0 +1,41 @@ +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); + allSecrets.map(x => console.log(x['type'], x['content'])); + 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; From f99bd74cd4a0f1cc7bc8aa3e8b44c1e3ade73aa1 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 14 Jul 2022 16:49:31 +0200 Subject: [PATCH 04/29] UI: Construct and render stolen credentials --- .../report-components/security/StolenPasswords.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js index 25a701871..d646974ec 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js @@ -1,13 +1,13 @@ import React from 'react'; -import ReactTable from 'react-table' +import ReactTable from 'react-table' +import {getCredentialsTableData} from '../credentialParsing.js'; const columns = [ { Header: 'Stolen Credentials', columns: [ {Header: 'Username', accessor: 'username'}, - {Header: 'Type', accessor: 'type'}, - {Header: 'Stolen From', accessor: 'origin'} + {Header: 'Type', accessor: 'type'} ] } ]; @@ -22,11 +22,12 @@ class StolenPasswordsComponent extends React.Component { render() { let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length; let showPagination = this.props.data.length > pageSize; + let table_data = getCredentialsTableData(this.props.data); return (
From 8ccdba7528fbdd8ff9a304c6ff35e6a2d1590ef4 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 14 Jul 2022 16:50:04 +0200 Subject: [PATCH 05/29] UI: Grab credentials from endpoint and render them --- .../report-components/SecurityReport.js | 108 ++++++++++++------ 1 file changed, 76 insertions(+), 32 deletions(-) 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..04b05f04f 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 @@ -45,6 +45,8 @@ import { zerologonOverviewWithFailedPassResetWarning } from './security/issues/ZerologonIssue'; import {powershellIssueOverview, powershellIssueReport} from './security/issues/PowershellIssue'; +import {getCredentialsSecrets, getCredentialsUsernames} from './credentialParsing'; +import UsedCredentials from './security/UsedCredentials.tsx'; class ReportPageComponent extends AuthComponent { @@ -161,15 +163,31 @@ class ReportPageComponent extends AuthComponent { this.state = { report: props.report, graph: {nodes: [], edges: []}, - nodeStateList: [] + nodeStateList: [], + stolenCredentials: [], + configuredCredentials: [] }; } 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,10 +202,19 @@ class ReportPageComponent extends AuthComponent { componentDidUpdate(prevProps) { if (this.props.report !== prevProps.report) { - this.setState({report: this.props.report}) + this.updateReport(this.props.report); } } + updateReport(report) { + let newReport = this.addIssuesToOverviewIssues(report); + + this.setState({ + report: newReport + }) + } + + render() { let content; @@ -273,39 +300,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: -

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

- Passwords used for brute-forcing: -

-
    - {this.state.report.overview.config_passwords.map(x =>
  • {x.substr(0, 3) + '******'}
  • )} -
- - : -

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

} { @@ -535,7 +543,7 @@ class ReportPageComponent extends AuthComponent {
- +
@@ -582,6 +590,42 @@ class ReportPageComponent extends AuthComponent { } return
    {issuesDivArray}
; }; + + addIssuesToOverviewIssues(report) { + let issues = report.overview.issues; + let overview_issues = []; + + for(let i=0; i < issues.length; i++) { + if (this.isWeakCredentialsIssue(issues[i])) { + overview_issues.push('weak_password') + } else if (this.isStolenCredentialsIssue(issues[i])) { + overview_issues.push('stolen_creds'); + } else { + overview_issues.push(issues[i]) + } + } + + const newOverview = { ...report.overview, issues : overview_issues }; + + const newReport = { ...report, overview : newOverview }; + + + return newReport; + } + + isWeakCredentialsIssue(issue) { + return (Object.prototype.hasOwnProperty.call(issue, 'credential_type') && + issue.credential_type === 'PASSWORD' && + getCredentialsSecrets(this.state.configuredCredentials, 'password').includes(issue.password) && + getCredentialsUsernames(this.state.configuredCredentials).includes(issue.username)) + } + + isStolenCredentialsIssue(issue) { + return ( Object.prototype.hasOwnProperty.call(issue, 'credential_type') && + (issue.credential_type === 'PASSWORD' || + issue.credential_type === 'NT_HASH' || + issue.credential_type === 'LM_HASH')) + } } export default ReportPageComponent; From 4dbdbcc75e023aa9dfbce10e28734b43497e195b Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 14 Jul 2022 17:03:14 +0200 Subject: [PATCH 06/29] UI: Remove unneeded logging in UsedCredentials component --- .../components/report-components/security/UsedCredentials.tsx.js | 1 - 1 file changed, 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js index b255a7ca9..7a5becde7 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js @@ -10,7 +10,6 @@ class UsedCredentials extends React.Component { render() { let allUsernames = getAllUsernames(this.props.stolen, this.props.configured); let allSecrets = getAllSecrets(this.props.stolen, this.props.configured); - allSecrets.map(x => console.log(x['type'], x['content'])); return ( allUsernames.length > 0 ? <> From 89f5ff89d29172b2b431126440188cd83c05254c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 14 Jul 2022 17:23:12 +0200 Subject: [PATCH 07/29] Island: Remove extract_ssh_keys from stolen_credentials reporting --- .../services/reporting/stolen_credentials.py | 4 --- .../reporting/test_stolen_credentials.py | 27 +------------------ 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/stolen_credentials.py b/monkey/monkey_island/cc/services/reporting/stolen_credentials.py index 2a817ac75..ebcc2190a 100644 --- a/monkey/monkey_island/cc/services/reporting/stolen_credentials.py +++ b/monkey/monkey_island/cc/services/reporting/stolen_credentials.py @@ -15,10 +15,6 @@ def get_stolen_creds() -> Sequence[Mapping]: 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()) 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 index d7c2386c9..201dda576 100644 --- 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 @@ -2,10 +2,7 @@ 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, -) +from monkey_island.cc.services.reporting.stolen_credentials import get_stolen_creds monkey_hostname = "fake_hostname" fake_monkey_guid = "abc" @@ -73,25 +70,3 @@ def test_get_credentials(fake_monkey): 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 From bfda71dd45163ff922aca2500f560012554002a8 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 11:03:41 +0200 Subject: [PATCH 08/29] UI: Check for actual stolen credentials in issues --- .../cc/ui/src/components/report-components/SecurityReport.js | 3 +++ 1 file changed, 3 insertions(+) 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 04b05f04f..e586a3ec6 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 @@ -622,6 +622,9 @@ class ReportPageComponent extends AuthComponent { isStolenCredentialsIssue(issue) { return ( Object.prototype.hasOwnProperty.call(issue, 'credential_type') && + (getCredentialsSecrets(this.state.stolenCredentials, 'password').includes(issue.password) || + getCredentialsSecrets(this.state.configuredCredentials, 'nt_hash').includes(issue.password) || + getCredentialsSecrets(this.state.configuredCredentials, 'lm_hash').includes(issue.password)) && (issue.credential_type === 'PASSWORD' || issue.credential_type === 'NT_HASH' || issue.credential_type === 'LM_HASH')) From f417cff17bc37f2b771cfdee79125b10e4974eb4 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 11:08:51 +0200 Subject: [PATCH 09/29] UI: Rename UsedCredentials.tsx.js to UsedCredentials.js --- .../cc/ui/src/components/report-components/SecurityReport.js | 2 +- .../security/{UsedCredentials.tsx.js => UsedCredentials.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename monkey/monkey_island/cc/ui/src/components/report-components/security/{UsedCredentials.tsx.js => UsedCredentials.js} (100%) 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 e586a3ec6..9196a0e2d 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 @@ -46,7 +46,7 @@ import { } from './security/issues/ZerologonIssue'; import {powershellIssueOverview, powershellIssueReport} from './security/issues/PowershellIssue'; import {getCredentialsSecrets, getCredentialsUsernames} from './credentialParsing'; -import UsedCredentials from './security/UsedCredentials.tsx'; +import UsedCredentials from './security/UsedCredentials'; class ReportPageComponent extends AuthComponent { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.js similarity index 100% rename from monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.tsx.js rename to monkey/monkey_island/cc/ui/src/components/report-components/security/UsedCredentials.js From 94419d81419f07267103df7b215b6332f6ffd87a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 11:12:12 +0200 Subject: [PATCH 10/29] Island: Add PTHReportservice get issues functions * get_duplicated_passwords_issues * get strong_users_on_crit_issues --- monkey/monkey_island/cc/services/reporting/report.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index a6d45e3e2..09418e9af 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -496,6 +496,8 @@ class ReportService: ReportService.get_exploits, ReportService.get_tunnels, ReportService.get_island_cross_segment_issues, + PTHReportService.get_duplicated_passwords_issues, + PTHReportService.get_strong_users_on_crit_issues, ] issues = functools.reduce(lambda acc, issue_gen: acc + issue_gen(), ISSUE_GENERATORS, []) From c75ee22c295abfb0d1e313468f755ed786dca110 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 11:15:06 +0200 Subject: [PATCH 11/29] Island: Remove get_config_{users,passwords} from reporting --- .../cc/services/reporting/report.py | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 09418e9af..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 @@ -375,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() From c83f76b02b9fe7da7baa525bf71669d51f48a5cb Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 15:22:30 +0200 Subject: [PATCH 12/29] Island: Add formatting credentials for report --- .../services/reporting/stolen_credentials.py | 35 +++------ .../reporting/test_stolen_credentials.py | 72 ++++++++----------- 2 files changed, 39 insertions(+), 68 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/stolen_credentials.py b/monkey/monkey_island/cc/services/reporting/stolen_credentials.py index ebcc2190a..9d01bde25 100644 --- a/monkey/monkey_island/cc/services/reporting/stolen_credentials.py +++ b/monkey/monkey_island/cc/services/reporting/stolen_credentials.py @@ -1,48 +1,35 @@ import logging from typing import Mapping, Sequence -from common.credentials import CredentialComponentType -from monkey_island.cc.models import StolenCredentials +from common.credentials import CredentialComponentType, Credentials logger = logging.getLogger(__name__) -def get_stolen_creds() -> Sequence[Mapping]: - stolen_creds = _fetch_from_db() - stolen_creds = _format_creds_for_reporting(stolen_creds) - +def format_creds_for_reporting(credentials: Sequence[Credentials]) -> Sequence[Mapping]: logger.info("Stolen creds generated for reporting") - return stolen_creds - -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", + 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: for secret_type in cred.secrets: - if secret_type not in cred_type_dict: + if secret_type.credential_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, + "_type": secret_type.credential_type.name, + "type": cred_type_dict[secret_type.credential_type], } 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 "" +def _get_username(credentials: Credentials) -> str: + return credentials.identities[0].username if credentials.identities else "" 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 index 201dda576..54dc31e7f 100644 --- 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 @@ -1,70 +1,54 @@ -import pytest - -from common.credentials import CredentialComponentType -from monkey_island.cc.models import Monkey, StolenCredentials -from monkey_island.cc.services.reporting.stolen_credentials import get_stolen_creds +from common.credentials import ( + CredentialComponentType, + Credentials, + LMHash, + NTHash, + Password, + SSHKeypair, + Username, +) +from monkey_island.cc.services.reporting.stolen_credentials import format_creds_for_reporting 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, - ], -} +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(identities, secrets)] -@pytest.fixture -def fake_monkey(): - monkey = Monkey() - monkey.guid = fake_monkey_guid - monkey.hostname = monkey_hostname - monkey.save() - return monkey.id +def test_formatting_credentials_for_report(): - -@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() + credentials = format_creds_for_reporting(fake_credentials) result1 = { - "origin": monkey_hostname, "_type": CredentialComponentType.NT_HASH.name, "type": "NTLM hash", - "username": fake_username, + "username": fake_username.username, } result2 = { - "origin": monkey_hostname, "_type": CredentialComponentType.LM_HASH.name, "type": "LM hash", - "username": fake_username, + "username": fake_username.username, } result3 = { - "origin": monkey_hostname, "_type": CredentialComponentType.PASSWORD.name, "type": "Clear Password", - "username": fake_username, + "username": fake_username.username, } result4 = { - "origin": monkey_hostname, "_type": CredentialComponentType.SSH_KEYPAIR.name, "type": "Clear SSH private key", - "username": fake_username, + "username": fake_username.username, } assert result1 in credentials assert result2 in credentials From 06a64c14d7117da5fde5dcc243367ba3f94ba848 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 15:23:50 +0200 Subject: [PATCH 13/29] Island: Add callable class for T1003 --- .../attack/technique_reports/T1003.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) 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..e1dbf4967 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.stolen_credentials 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 From 885f0565a834d15146f4635cb115ba6bfba530d6 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 15:24:25 +0200 Subject: [PATCH 14/29] Island: Patch T1003 with a callable class --- monkey/monkey_island/cc/services/initialize.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index 26209ce6c..dbbd057b5 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -41,6 +41,7 @@ from monkey_island.cc.services.telemetry.processing.processing import ( TELEMETRY_CATEGORY_TO_PROCESSING_FUNC, ) from monkey_island.cc.setup.mongo.mongo_setup import MONGO_URL +from monkey_island.services.atack.technique_reports.T1003 import T1003, T1003GetReportData from . import AuthenticationService from .reporting.report import ReportService @@ -64,6 +65,8 @@ def initialize_services(data_dir: Path) -> DIContainer: _register_repositories(container, data_dir) _register_services(container) + _dirty_hacks(container) + # Note: A hack to resolve credentials parser # It changes telemetry processing function, this will be refactored! _patch_credentials_parser(container) @@ -157,3 +160,12 @@ def _patch_credentials_parser(container: DIContainer): TELEMETRY_CATEGORY_TO_PROCESSING_FUNC[TelemCategoryEnum.CREDENTIALS] = container.resolve( CredentialsParser ) + + +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) From a24bdd43b8c3eb1abdeaf6b178b140049981e9d1 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 15:26:38 +0200 Subject: [PATCH 15/29] Island: Move credentials parser hack to dirty_hacks function --- monkey/monkey_island/cc/services/initialize.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index dbbd057b5..f9f9b3ff0 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -67,10 +67,6 @@ def initialize_services(data_dir: Path) -> DIContainer: _dirty_hacks(container) - # Note: A hack to resolve credentials parser - # It changes telemetry processing function, this will be refactored! - _patch_credentials_parser(container) - # This is temporary until we get DI all worked out. ReportService.initialize( container.resolve(AWSService), @@ -156,12 +152,6 @@ def _register_services(container: DIContainer): container.register_instance(RepositoryService, container.resolve(RepositoryService)) -def _patch_credentials_parser(container: DIContainer): - TELEMETRY_CATEGORY_TO_PROCESSING_FUNC[TelemCategoryEnum.CREDENTIALS] = container.resolve( - CredentialsParser - ) - - def _dirty_hacks(container: DIContainer): # A dirty hacks function that patches some of the things that # are needed at the current point @@ -169,3 +159,9 @@ def _dirty_hacks(container: DIContainer): # 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 + ) From 5ebf0ed8f6654281b27aa45f90f60b57d17f9b7c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 15:27:57 +0200 Subject: [PATCH 16/29] Island: Remove StolenCredentials model --- monkey/monkey_island/cc/models/__init__.py | 1 - .../cc/models/stolen_credentials.py | 21 ------------------- 2 files changed, 22 deletions(-) delete mode 100644 monkey/monkey_island/cc/models/stolen_credentials.py 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() From 168326586834d028e24818777ec9104277705972 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 15:32:40 +0200 Subject: [PATCH 17/29] Island: Rename reporting/stolen_credentials.py to reporting/format_credentials.py --- .../monkey_island/cc/services/attack/technique_reports/T1003.py | 2 +- monkey/monkey_island/cc/services/reporting/__init__.py | 1 + .../reporting/{stolen_credentials.py => format_credentials.py} | 0 .../{test_stolen_credentials.py => test_format_credentials.py} | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) rename monkey/monkey_island/cc/services/reporting/{stolen_credentials.py => format_credentials.py} (100%) rename monkey/tests/unit_tests/monkey_island/cc/services/reporting/{test_stolen_credentials.py => test_format_credentials.py} (94%) 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 e1dbf4967..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.repository import ICredentialsRepository from monkey_island.cc.services.attack.technique_reports import AttackTechnique -from monkey_island.cc.services.reporting.stolen_credentials import format_creds_for_reporting +from monkey_island.cc.services.reporting import format_creds_for_reporting class T1003(AttackTechnique): 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/stolen_credentials.py b/monkey/monkey_island/cc/services/reporting/format_credentials.py similarity index 100% rename from monkey/monkey_island/cc/services/reporting/stolen_credentials.py rename to monkey/monkey_island/cc/services/reporting/format_credentials.py 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_format_credentials.py similarity index 94% rename from monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_stolen_credentials.py rename to monkey/tests/unit_tests/monkey_island/cc/services/reporting/test_format_credentials.py index 54dc31e7f..089dc5d5d 100644 --- 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_format_credentials.py @@ -7,7 +7,7 @@ from common.credentials import ( SSHKeypair, Username, ) -from monkey_island.cc.services.reporting.stolen_credentials import format_creds_for_reporting +from monkey_island.cc.services.reporting import format_creds_for_reporting monkey_hostname = "fake_hostname" fake_monkey_guid = "abc" From c65439e049f97ea4f98e92fbf3d4e19e3b84a10e Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 15:58:22 +0200 Subject: [PATCH 18/29] UI: Remove WeakPassword issue * We don't have the passwords used for exploiting the machines in the UI * All it will be reworked --- .../report-components/SecurityReport.js | 22 ++----------------- .../security/issues/WeakPasswordIssue.js | 6 ----- 2 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 monkey/monkey_island/cc/ui/src/components/report-components/security/issues/WeakPasswordIssue.js 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 9196a0e2d..929147a1c 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,7 +44,7 @@ import { zerologonOverviewWithFailedPassResetWarning } from './security/issues/ZerologonIssue'; import {powershellIssueOverview, powershellIssueReport} from './security/issues/PowershellIssue'; -import {getCredentialsSecrets, getCredentialsUsernames} from './credentialParsing'; +import {getCredentialsSecrets} from './credentialParsing'; import UsedCredentials from './security/UsedCredentials'; @@ -148,10 +147,6 @@ 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 - }, 'stolen_creds': { [this.issueContentTypes.OVERVIEW]: stolenCredsIssueOverview, [this.issueContentTypes.TYPE]: this.issueTypes.DANGER @@ -596,30 +591,17 @@ class ReportPageComponent extends AuthComponent { let overview_issues = []; for(let i=0; i < issues.length; i++) { - if (this.isWeakCredentialsIssue(issues[i])) { - overview_issues.push('weak_password') - } else if (this.isStolenCredentialsIssue(issues[i])) { + if (this.isStolenCredentialsIssue(issues[i])) { overview_issues.push('stolen_creds'); } else { overview_issues.push(issues[i]) } } - const newOverview = { ...report.overview, issues : overview_issues }; - const newReport = { ...report, overview : newOverview }; - - return newReport; } - isWeakCredentialsIssue(issue) { - return (Object.prototype.hasOwnProperty.call(issue, 'credential_type') && - issue.credential_type === 'PASSWORD' && - getCredentialsSecrets(this.state.configuredCredentials, 'password').includes(issue.password) && - getCredentialsUsernames(this.state.configuredCredentials).includes(issue.username)) - } - isStolenCredentialsIssue(issue) { return ( Object.prototype.hasOwnProperty.call(issue, 'credential_type') && (getCredentialsSecrets(this.state.stolenCredentials, 'password').includes(issue.password) || 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.
  • ) -} From 35ed7f60c41f10a299f817172f069ea9e6cb729a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 16:00:19 +0200 Subject: [PATCH 19/29] Island: Fix an import in initialize --- monkey/monkey_island/cc/services/initialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index f9f9b3ff0..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, @@ -41,7 +42,6 @@ from monkey_island.cc.services.telemetry.processing.processing import ( TELEMETRY_CATEGORY_TO_PROCESSING_FUNC, ) from monkey_island.cc.setup.mongo.mongo_setup import MONGO_URL -from monkey_island.services.atack.technique_reports.T1003 import T1003, T1003GetReportData from . import AuthenticationService from .reporting.report import ReportService From 5c765f85c2e72d38ad45cb0f86ce452a42480839 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 16:04:51 +0200 Subject: [PATCH 20/29] UI: Add StolenCredentialsIssue to issues --- .../components/report-components/SecurityReport.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) 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 929147a1c..17e0dc645 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 @@ -44,7 +44,6 @@ import { zerologonOverviewWithFailedPassResetWarning } from './security/issues/ZerologonIssue'; import {powershellIssueOverview, powershellIssueReport} from './security/issues/PowershellIssue'; -import {getCredentialsSecrets} from './credentialParsing'; import UsedCredentials from './security/UsedCredentials'; @@ -591,7 +590,7 @@ class ReportPageComponent extends AuthComponent { let overview_issues = []; for(let i=0; i < issues.length; i++) { - if (this.isStolenCredentialsIssue(issues[i])) { + if (this.shouldAddStolenCredentialsIssue()) { overview_issues.push('stolen_creds'); } else { overview_issues.push(issues[i]) @@ -602,14 +601,8 @@ class ReportPageComponent extends AuthComponent { return newReport; } - isStolenCredentialsIssue(issue) { - return ( Object.prototype.hasOwnProperty.call(issue, 'credential_type') && - (getCredentialsSecrets(this.state.stolenCredentials, 'password').includes(issue.password) || - getCredentialsSecrets(this.state.configuredCredentials, 'nt_hash').includes(issue.password) || - getCredentialsSecrets(this.state.configuredCredentials, 'lm_hash').includes(issue.password)) && - (issue.credential_type === 'PASSWORD' || - issue.credential_type === 'NT_HASH' || - issue.credential_type === 'LM_HASH')) + shouldAddStolenCredentialsIssue() { + return ( this.state.stolenCredentials.length > 0 ) } } From 474a26aeffd81f362d3fbf26160526d84791a4be Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 16:29:02 +0200 Subject: [PATCH 21/29] UI: Fix StolenCredentials issue to add if we have any stolen credentials --- .../components/report-components/SecurityReport.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 17e0dc645..4248a97fc 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 @@ -587,14 +587,10 @@ class ReportPageComponent extends AuthComponent { addIssuesToOverviewIssues(report) { let issues = report.overview.issues; - let overview_issues = []; + let overview_issues = issues; - for(let i=0; i < issues.length; i++) { - if (this.shouldAddStolenCredentialsIssue()) { - overview_issues.push('stolen_creds'); - } else { - overview_issues.push(issues[i]) - } + if (this.shouldAddStolenCredentialsIssue()) { + overview_issues.push('stolen_creds'); } const newOverview = { ...report.overview, issues : overview_issues }; const newReport = { ...report, overview : newOverview }; @@ -602,6 +598,8 @@ class ReportPageComponent extends AuthComponent { } shouldAddStolenCredentialsIssue() { + // TODO: This should check if any stolen credentials are used to + // exploit a machine return ( this.state.stolenCredentials.length > 0 ) } } From 27c0b838c4aebce02f684e00a50fe0fe524ce062 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 15 Jul 2022 17:40:43 +0200 Subject: [PATCH 22/29] Island: Fix one missed telemetry processor --- .../telemetry/processing/credentials/credentials_parser.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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"] ] From 5e1adbb877d339d15c17a644cf243630c792ffe4 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 18 Jul 2022 08:27:35 +0200 Subject: [PATCH 23/29] UI: Add formatting to StolenPasswordsComponent This component was used in security and attack report with two different sets of data. The first one is from the credentials endpoint which needed formatting and the second from the telemetry which was already formatted. --- .../cc/ui/src/components/attack/techniques/T1003.js | 4 +++- .../ui/src/components/report-components/SecurityReport.js | 5 ++++- .../report-components/security/StolenPasswords.js | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) 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 4248a97fc..61f5dfc60 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 @@ -537,7 +537,10 @@ class ReportPageComponent extends AuthComponent {
    - +
    diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js index d646974ec..b199d131d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js @@ -22,7 +22,11 @@ class StolenPasswordsComponent extends React.Component { render() { let defaultPageSize = this.props.data.length > pageSize ? pageSize : this.props.data.length; let showPagination = this.props.data.length > pageSize; - let table_data = getCredentialsTableData(this.props.data); + let table_data = this.props.data; + if(this.props.format) { + table_data = getCredentialsTableData(this.props.data); + } + return (
    Date: Mon, 18 Jul 2022 08:44:38 +0200 Subject: [PATCH 24/29] UI: Set stolen_creds issues in state --- .../report-components/SecurityReport.js | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) 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 61f5dfc60..a98efb3a0 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 @@ -159,7 +159,8 @@ class ReportPageComponent extends AuthComponent { graph: {nodes: [], edges: []}, nodeStateList: [], stolenCredentials: [], - configuredCredentials: [] + configuredCredentials: [], + issues: [] }; } @@ -196,19 +197,11 @@ class ReportPageComponent extends AuthComponent { componentDidUpdate(prevProps) { if (this.props.report !== prevProps.report) { - this.updateReport(this.props.report); + this.setState({report: this.props.report}); + this.addIssuesToOverviewIssues(); } } - updateReport(report) { - let newReport = this.addIssuesToOverviewIssues(report); - - this.setState({ - report: newReport - }) - } - - render() { let content; @@ -381,6 +374,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])) { @@ -422,6 +418,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])) { @@ -441,6 +441,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])) { @@ -588,16 +592,15 @@ class ReportPageComponent extends AuthComponent { return
      {issuesDivArray}
    ; }; - addIssuesToOverviewIssues(report) { - let issues = report.overview.issues; - let overview_issues = issues; + addIssuesToOverviewIssues() { + let overview_issues = this.state.issues; if (this.shouldAddStolenCredentialsIssue()) { overview_issues.push('stolen_creds'); } - const newOverview = { ...report.overview, issues : overview_issues }; - const newReport = { ...report, overview : newOverview }; - return newReport; + this.setState({ + issues: overview_issues + }); } shouldAddStolenCredentialsIssue() { From 67e67441c171b8c0053fe08c745e5eb605283caf Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 18 Jul 2022 12:10:19 +0200 Subject: [PATCH 25/29] UI: Remove unused getCredenatislSecrets function --- .../components/report-components/credentialParsing.js | 9 --------- 1 file changed, 9 deletions(-) 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 index 73ece9502..9a6eee59f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js @@ -5,15 +5,6 @@ export function getAllUsernames(stolen, configured){ return usernames; } -export function getCredentialsSecrets(credentials, credential_type) { - let secrets = []; - - for(let i = 0; i < credentials.length; i++){ - secrets.push(credentials[i]['secrets'][0][credential_type]); - } - return secrets; -} - export function getCredentialsUsernames(credentials) { let usernames = []; for(let i = 0; i < credentials.length; i++){ From c56b38f695c399eee075d1604c0a989ba5320144 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 18 Jul 2022 12:14:31 +0200 Subject: [PATCH 26/29] UI: Add note in StolenPasswords component --- .../components/report-components/security/StolenPasswords.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js index b199d131d..441bac8b1 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js @@ -24,6 +24,8 @@ class StolenPasswordsComponent extends React.Component { 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); } From 57f2c7e05842f1fffc97dbe053ee48f508c7232b Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 18 Jul 2022 21:48:47 +0200 Subject: [PATCH 27/29] Island: Fix credentials formatting to use simplified credentials object --- .../services/reporting/format_credentials.py | 27 ++++++++++--------- .../reporting/test_format_credentials.py | 15 ++++++++++- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/monkey/monkey_island/cc/services/reporting/format_credentials.py b/monkey/monkey_island/cc/services/reporting/format_credentials.py index 9d01bde25..721868cdc 100644 --- a/monkey/monkey_island/cc/services/reporting/format_credentials.py +++ b/monkey/monkey_island/cc/services/reporting/format_credentials.py @@ -17,19 +17,22 @@ def format_creds_for_reporting(credentials: Sequence[Credentials]) -> Sequence[M CredentialComponentType.SSH_KEYPAIR: "Clear SSH private key", } for cred in credentials: - for secret_type in cred.secrets: - if secret_type.credential_type not in cred_type_dict: - continue - username = _get_username(cred) - cred_row = { - "username": username, - "_type": secret_type.credential_type.name, - "type": cred_type_dict[secret_type.credential_type], - } - if cred_row not in formatted_creds: - formatted_creds.append(cred_row) + 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.identities[0].username if credentials.identities else "" + return credentials.identity.username if credentials.identity else "" 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 index 089dc5d5d..bb51b89dd 100644 --- 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 @@ -23,7 +23,14 @@ 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(identities, secrets)] +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(): @@ -50,7 +57,13 @@ def test_formatting_credentials_for_report(): "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 From b3ec9e340f096428add41dd167011c005b2d3b73 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 18 Jul 2022 21:49:24 +0200 Subject: [PATCH 28/29] UI: Fix Credentials parsing to use simplified credentials object --- .../src/components/report-components/credentialParsing.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 index 9a6eee59f..ea95a88d8 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/credentialParsing.js @@ -8,7 +8,7 @@ export function getAllUsernames(stolen, configured){ export function getCredentialsUsernames(credentials) { let usernames = []; for(let i = 0; i < credentials.length; i++){ - usernames.push(credentials[i]['identities'][0]['username']); + usernames.push(credentials[i]['identity']['username']); } return usernames; } @@ -16,10 +16,10 @@ export function getCredentialsUsernames(credentials) { export function getAllSecrets(stolen, configured){ let secrets = []; for(let i = 0; i < stolen.length; i++){ - secrets.push(getSecretsFromCredential(stolen[i]['secrets'][0])); + secrets.push(getSecretsFromCredential(stolen[i]['secret'])); } for(let i = 0; i < configured.length; i++){ - secrets.push(getSecretsFromCredential(configured[i]['secrets'][0])); + secrets.push(getSecretsFromCredential(configured[i]['secret'])); } return secrets; } From cc021f33ff81d65382efa93316a30b0db76efd8c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 19 Jul 2022 10:15:22 +0200 Subject: [PATCH 29/29] UI: Add note about removal of 'weak_password' issue --- .../cc/ui/src/components/report-components/SecurityReport.js | 2 ++ 1 file changed, 2 insertions(+) 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 a98efb3a0..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 @@ -146,6 +146,8 @@ class ReportPageComponent extends AuthComponent { [this.issueContentTypes.REPORT]: strongUsersOnCritIssueReport, [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