diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py index 8628136d8..eff1f7758 100644 --- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py @@ -7,6 +7,8 @@ from monkey_island.cc.services.reporting.issue_processing.exploit_processing.pro from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ExploitProcessor from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.shellshock_exploit import \ ShellShockExploitProcessor +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.zerologon import \ + ZerologonExploitProcessor @dataclass @@ -31,3 +33,4 @@ class ExploiterDescriptorEnum(Enum): MSSQL = ExploiterDescriptor('MSSQLExploiter', 'MSSQL Exploiter', ExploitProcessor) VSFTPD = ExploiterDescriptor('VSFTPDExploiter', 'VSFTPD Backdoor Exploiter', CredExploitProcessor) DRUPAL = ExploiterDescriptor('DrupalExploiter', 'Drupal Server Exploiter', ExploitProcessor) + ZEROLOGON = ExploiterDescriptor('ZerologonExploiter', 'ZeroLogon Exploiter', ZerologonExploitProcessor) diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py new file mode 100644 index 000000000..e0be6cd42 --- /dev/null +++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/zerologon.py @@ -0,0 +1,11 @@ +from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ExploitProcessor, \ + ExploiterReportInfo + + +class ZerologonExploitProcessor: + + @staticmethod + def get_exploit_info_by_dict(class_name: str, exploit_dict: dict) -> ExploiterReportInfo: + exploit_info = ExploitProcessor.get_exploit_info_by_dict(class_name, exploit_dict) + exploit_info.password_restored = exploit_dict['data']['info']['password_restored'] + return exploit_info diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 42adfd5a2..a4642c694 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -45,6 +45,7 @@ class ReportService: class DerivedIssueEnum: WEAK_PASSWORD = "weak_password" STOLEN_CREDS = "stolen_creds" + ZEROLOGON_PASS_RESTORE_FAILED = "zerologon_pass_restore_failed" @staticmethod def get_first_monkey_time(): @@ -264,7 +265,7 @@ class ReportService: return exploiter_info @staticmethod - def get_exploits() -> dict: + def get_exploits() -> List[dict]: query = [{'$match': {'telem_category': 'exploit', 'data.result': True}}, {'$group': {'_id': {'ip_address': '$data.machine.ip_addr'}, 'data': {'$first': '$$ROOT'}, @@ -439,7 +440,6 @@ class ReportService: @staticmethod def get_domain_issues(): - ISSUE_GENERATORS = [ PTHReportService.get_duplicated_passwords_issues, PTHReportService.get_shared_admins_issues, @@ -491,8 +491,7 @@ class ReportService: if exploits == default_exploits: return ['default'] - # TODO investigate strange code - return [exploit for exploit in exploits] + return exploits @staticmethod def get_config_ips(): @@ -508,13 +507,12 @@ class ReportService: for machine in issues: for issue in issues[machine]: - # TODO check if this actually works, because stolen passwords get added to config - # so any password will be in config. We need to separate stolen passwords from initial - # passwords in config. 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): + issue_set.add(ReportService.DerivedIssueEnum.ZEROLOGON_PASS_RESTORE_FAILED) issue_set.add(issue['type']) @@ -535,6 +533,11 @@ class ReportService: (issue['credential_type'] == CredentialType.PASSWORD.value or issue['credential_type'] == CredentialType.HASH.value) + @staticmethod + def _is_zerologon_pass_restore_failed(issue: dict): + return issue['type'] == ExploiterDescriptorEnum.ZEROLOGON.value.class_name \ + and not issue['password_restored'] + @staticmethod def is_report_generated(): generated_report = mongo.db.report.find_one({}) @@ -594,7 +597,6 @@ class ReportService: @staticmethod def get_issues(): - # Todo refactor these into separate files with a method signature -> dict ISSUE_GENERATORS = [ ReportService.get_exploits, ReportService.get_tunnels, 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 4296e773c..cdd23aa8a 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,6 +46,11 @@ import {StolenCredsIssueOverview} from './security/issues/StolenCredsIssue'; import {WeakPasswordIssueOverview} from './security/issues/WeakPasswordIssue'; import {AzurePasswordIssueOverview, AzurePasswordIssueReport} from './security/issues/AzurePasswordIssue'; import {generateStrongUsersOnCritIssue} from './security/issues/StrongUsersOnCritIssue'; +import { + ZerologonIssueOverview, + ZerologonIssueReport, + ZerologonOverviewWithFailedPassResetWarning +} from './security/issues/ZerologonIssue'; class ReportPageComponent extends AuthComponent { @@ -139,7 +144,12 @@ class ReportPageComponent extends AuthComponent { [this.issueContentTypes.TYPE]: this.issueTypes.DANGER }, 'ZerologonExploiter': { - //TODO add + [this.issueContentTypes.OVERVIEW]: ZerologonIssueOverview, + [this.issueContentTypes.REPORT]: ZerologonIssueReport, + [this.issueContentTypes.TYPE]: this.issueTypes.DANGER + }, + 'zerologon_pass_restore_failed': { + [this.issueContentTypes.OVERVIEW]: ZerologonOverviewWithFailedPassResetWarning, }, 'island_cross_segment': { [this.issueContentTypes.OVERVIEW]: crossSegmentIssueOverview, @@ -162,11 +172,9 @@ class ReportPageComponent extends AuthComponent { [this.issueContentTypes.TYPE]: this.issueTypes.WARNING }, 'shared_passwords_domain': { - [this.issueContentTypes.REPORT]: generateSharedCredsDomainIssue(), + [this.issueContentTypes.REPORT]: generateSharedCredsDomainIssue, [this.issueContentTypes.TYPE]: this.issueTypes.WARNING }, - - // This issue was missing overview section 'strong_users_on_crit': { [this.issueContentTypes.REPORT]: generateStrongUsersOnCritIssue, [this.issueContentTypes.TYPE]: this.issueTypes.DANGER @@ -224,8 +232,6 @@ class ReportPageComponent extends AuthComponent { if (this.stillLoadingDataFromServer()) { content = ; } else { - - console.log(this.state.report); content =
{this.generateReportOverviewSection()} @@ -386,7 +392,7 @@ class ReportPageComponent extends AuthComponent {
The Monkey uncovered the following possible set of issues:
    - {overviews} + {this.getPotentialSecurityIssuesOverviews()}
: @@ -416,15 +422,18 @@ class ReportPageComponent extends AuthComponent { getPotentialSecurityIssuesOverviews() { let overviews = []; + let issues = this.state.report.overview.issues; - for (let issueKey of this.state.report.overview.issues) { - overviews.push(this.IssueDescriptorEnum[issueKey][this.issueContentTypes.OVERVIEW]); + for(let i=0; i < issues.length; i++) { + if (this.isIssuePotentialSecurityIssue(issues[i])) { + overviews.push(this.getIssueOverview(this.IssueDescriptorEnum[issues[i]])); + } } return overviews; } getImmediateThreats() { - let threatCount = this.countImmediateThreats() + let threatCount = this.getImmediateThreatCount() return (

@@ -436,18 +445,19 @@ class ReportPageComponent extends AuthComponent { {threatCount} threats : + {this.getImmediateThreatsOverviews()} }

) } - countImmediateThreats() { + getImmediateThreatCount() { let threatCount = 0; let issues = this.state.report.overview.issues; for(let i=0; i < issues.length; i++) { - if (this.IssueDescriptorEnum[issues[i]][this.issueContentTypes.TYPE] === this.issueTypes.DANGER) { + if(this.isIssueImmediateThreat(issues[i])) { threatCount++; } } @@ -551,8 +561,7 @@ class ReportPageComponent extends AuthComponent { generateIssue = (issue) => { let issueDescriptor = this.IssueDescriptorEnum[issue.type]; - let reportFnc = (issue) => { - }; + let reportFnc = (issue) => {}; if (issue.hasOwnProperty('credential_type')) { reportFnc = issueDescriptor[this.issueContentTypes.REPORT][issue.credential_type]; } else {