diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 95136b946..43bdeb411 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_config import AttackConfig from monkey_island.cc.database import mongo @@ -10,7 +10,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..fa65a66c2 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -0,0 +1,44 @@ +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_category': '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()} + successful_logins = list(mongo.db.telemetry.aggregate(T1075.query)) + data.update({'successful_logins': successful_logins}) + if successful_logins: + status = ScanStatus.USED + elif mongo.db.telemetry.count_documents(T1075.login_attempt_query): + status = ScanStatus.SCANNED + else: + 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 7fe5ac90f..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(T1110, ScanStatus.USED) + status = ScanStatus.USED elif attempts: - data = T1110.get_message_and_status(T1110, ScanStatus.SCANNED) + status = ScanStatus.SCANNED else: - data = T1110.get_message_and_status(T1110, 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/T1197.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1197.py index a4546f31c..9a968e998 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.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'}, 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 e8d0a64d4..6e89bc6ab 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 7a14f9da8..afcd67817 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -46,52 +46,63 @@ class AttackTechnique(object): """ pass - @staticmethod - def technique_status(technique): + @classmethod + def technique_status(cls): """ Gets the status of a certain attack technique. - :param technique: technique's id. :return: ScanStatus Enum object """ - if mongo.db.telemetry.find_one({'telem_category': 'attack', 'data.status': ScanStatus.USED.value, 'data.technique': technique}): + if mongo.db.attack_results.find_one({'telem_category': 'attack', + 'status': ScanStatus.USED.value, + 'technique': cls.tech_id}): return ScanStatus.USED - elif mongo.db.telemetry.find_one({'telem_category': 'attack', 'data.status': ScanStatus.SCANNED.value, 'data.technique': technique}): + elif mongo.db.attack_results.find_one({'telem_category': 'attack', + 'status': ScanStatus.SCANNED.value, + 'technique': cls.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): + """ + 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 + + @classmethod + def technique_title(cls): """ - :param technique: Technique's id. E.g. T1110 :return: techniques title. E.g. "T1110 Brute force" """ - return AttackConfig.get_technique(technique)['title'] + return AttackConfig.get_technique(cls.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 = cls.technique_status() + title = cls.technique_title() 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 new file mode 100644 index 000000000..785e4a295 --- /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..35fd0cbaa --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js @@ -0,0 +1,49 @@ +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) => 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: '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 ( +