diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 8b57eaec2..5970a33b7 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -198,25 +198,29 @@ class ReportService: for telem in mongo.db.telemetry.find({'telem_category': 'system_info', 'data.credentials': {'$exists': True}}, {'data.credentials': 1, 'monkey_guid': 1}): creds = telem['data']['credentials'] - formatted_creds.extend(ReportService._format_creds_for_reporting(telem, creds)) + origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname'] + formatted_creds.extend(ReportService._format_creds_for_reporting(telem, creds, origin)) return formatted_creds @staticmethod def _get_credentials_from_exploit_telems(): formatted_creds = [] for telem in mongo.db.telemetry.find({'telem_category': 'exploit', 'data.info.credentials': {'$exists': True}}, - {'data.info.credentials': 1, 'monkey_guid': 1}): + {'data.info.credentials': 1, 'data.machine': 1, 'monkey_guid': 1}): creds = telem['data']['info']['credentials'] - formatted_creds.extend(ReportService._format_creds_for_reporting(telem, creds)) + domain_name = telem['data']['machine']['domain_name'] + ip = telem['data']['machine']['ip_addr'] + origin = domain_name if domain_name else ip + formatted_creds.extend(ReportService._format_creds_for_reporting(telem, creds, origin)) return formatted_creds @staticmethod - def _format_creds_for_reporting(telem, monkey_creds): + def _format_creds_for_reporting(telem, monkey_creds, origin): creds = [] CRED_TYPE_DICT = {'password': 'Clear Password', 'lm_hash': 'LM hash', 'ntlm_hash': 'NTLM hash'} if len(monkey_creds) == 0: return [] - origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname'] + for user in monkey_creds: for cred_type in CRED_TYPE_DICT: if cred_type not in monkey_creds[user] or not monkey_creds[user][cred_type]: diff --git a/monkey/monkey_island/cc/services/tests/reporting/test_report.py b/monkey/monkey_island/cc/services/tests/reporting/test_report.py new file mode 100644 index 000000000..cc0ea321e --- /dev/null +++ b/monkey/monkey_island/cc/services/tests/reporting/test_report.py @@ -0,0 +1,118 @@ +import mongomock +import pytest +from bson import ObjectId + +from monkey_island.cc.services.reporting.report import ReportService + +TELEM_ID = { + "exploit_creds": ObjectId(b"123456789000"), + "system_info_creds": ObjectId(b"987654321000"), + "no_creds": ObjectId(b"112233445566"), + "monkey": ObjectId(b"665544332211"), +} +MONKEY_GUID = "67890" +USER = "user-name" +PWD = "password123" +LM_HASH = "e52cac67419a9a22664345140a852f61" +NT_HASH = "a9fdfa038c4b75ebc76dc855dd74f0da" +VICTIM_IP = "0.0.0.0" +VICTIM_DOMAIN_NAME = "domain-name" +HOSTNAME = "name-of-host" +EXPLOITER_CLASS_NAME = "exploiter-name" + +# Below telem constants only contain fields relevant to current tests + +EXPLOIT_TELEMETRY_TELEM = { + "_id": TELEM_ID["exploit_creds"], + "monkey_guid": MONKEY_GUID, + "telem_category": "exploit", + "data": { + "machine": { + "ip_addr": VICTIM_IP, + "domain_name": VICTIM_DOMAIN_NAME, + }, + "info": { + "credentials": { + USER: { + "username": USER, + "lm_hash": LM_HASH, + "ntlm_hash": NT_HASH, + } + } + } + } +} + + +SYSTEM_INFO_TELEMETRY_TELEM = { + "_id": TELEM_ID["system_info_creds"], + "monkey_guid": MONKEY_GUID, + "telem_category": "system_info", + "data": { + "credentials": { + USER: { + "password": PWD, + "lm_hash": LM_HASH, + "ntlm_hash": NT_HASH, + } + } + } +} + +NO_CREDS_TELEMETRY_TELEM = { + "_id": TELEM_ID["no_creds"], + "monkey_guid": MONKEY_GUID, + "telem_category": "exploit", + "data": { + "machine": { + "ip_addr": VICTIM_IP, + "domain_name": VICTIM_DOMAIN_NAME, + }, + "info": {"credentials": {}} + } +} + +MONKEY_TELEM = {"_id": TELEM_ID["monkey"], "guid": MONKEY_GUID, "hostname": HOSTNAME} + + +@pytest.fixture +def fake_mongo(monkeypatch): + mongo = mongomock.MongoClient() + monkeypatch.setattr("monkey_island.cc.services.reporting.report.mongo", mongo) + monkeypatch.setattr("monkey_island.cc.services.node.mongo", mongo) + return mongo + + +def test_get_stolen_creds_exploit(fake_mongo): + fake_mongo.db.telemetry.insert_one(EXPLOIT_TELEMETRY_TELEM) + + stolen_creds_exploit = ReportService.get_stolen_creds() + expected_stolen_creds_exploit = [ + {"origin": VICTIM_DOMAIN_NAME, "type": "LM hash", "username": USER}, + {"origin": VICTIM_DOMAIN_NAME, "type": "NTLM hash", "username": USER}, + ] + + assert expected_stolen_creds_exploit == stolen_creds_exploit + + +def test_get_stolen_creds_system_info(fake_mongo): + fake_mongo.db.monkey.insert_one(MONKEY_TELEM) + fake_mongo.db.telemetry.insert_one(SYSTEM_INFO_TELEMETRY_TELEM) + + stolen_creds_system_info = ReportService.get_stolen_creds() + expected_stolen_creds_system_info = [ + {"origin": HOSTNAME, "type": "Clear Password", "username": USER}, + {"origin": HOSTNAME, "type": "LM hash", "username": USER}, + {"origin": HOSTNAME, "type": "NTLM hash", "username": USER}, + ] + + assert expected_stolen_creds_system_info == stolen_creds_system_info + + +def test_get_stolen_creds_no_creds(fake_mongo): + fake_mongo.db.telemetry.insert_one(NO_CREDS_TELEMETRY_TELEM) + + stolen_creds_no_creds = ReportService.get_stolen_creds() + expected_stolen_creds_no_creds = [] + + assert expected_stolen_creds_no_creds == stolen_creds_no_creds