From 79d042b47112b6843520b0223b4a5c6a8ec3309b Mon Sep 17 00:00:00 2001 From: Shreya Date: Thu, 8 Jul 2021 15:44:43 +0530 Subject: [PATCH 01/11] island: Create RansomwareReportService and add `get_exploitation_stats()` to it --- .../monkey_island/cc/resources/ransomware_report.py | 3 ++- .../monkey_island/cc/services/ransomware_report.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 monkey/monkey_island/cc/services/ransomware_report.py diff --git a/monkey/monkey_island/cc/resources/ransomware_report.py b/monkey/monkey_island/cc/resources/ransomware_report.py index 55da76b06..f0232e72e 100644 --- a/monkey/monkey_island/cc/resources/ransomware_report.py +++ b/monkey/monkey_island/cc/resources/ransomware_report.py @@ -2,9 +2,10 @@ import flask_restful from flask import jsonify from monkey_island.cc.resources.auth.auth import jwt_required +from monkey_island.cc.services.ransomware_report import RansomwareReportService class RansomwareReport(flask_restful.Resource): @jwt_required def get(self): - return jsonify({"report": None}) + return jsonify({"report": None, "stats": RansomwareReportService.get_exploitation_stats()}) diff --git a/monkey/monkey_island/cc/services/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware_report.py new file mode 100644 index 000000000..93def94d7 --- /dev/null +++ b/monkey/monkey_island/cc/services/ransomware_report.py @@ -0,0 +1,13 @@ +from monkey_island.cc.services.reporting.report import ReportService + + +class RansomwareReportService: + def __init__(self): + pass + + @staticmethod + def get_exploitation_stats(): + scanned = ReportService.get_scanned() + exploited = ReportService.get_exploited() + + return {"scanned": scanned, "exploited": exploited} From d7ec2db47725633ad1cf36cc3605a28c93048937 Mon Sep 17 00:00:00 2001 From: Shreya Date: Thu, 8 Jul 2021 15:46:27 +0530 Subject: [PATCH 02/11] island: Rename `get_exploitation_stats()` to `get_exploitation_details()` --- monkey/monkey_island/cc/resources/ransomware_report.py | 4 +++- monkey/monkey_island/cc/services/ransomware_report.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/resources/ransomware_report.py b/monkey/monkey_island/cc/resources/ransomware_report.py index f0232e72e..96abfe252 100644 --- a/monkey/monkey_island/cc/resources/ransomware_report.py +++ b/monkey/monkey_island/cc/resources/ransomware_report.py @@ -8,4 +8,6 @@ from monkey_island.cc.services.ransomware_report import RansomwareReportService class RansomwareReport(flask_restful.Resource): @jwt_required def get(self): - return jsonify({"report": None, "stats": RansomwareReportService.get_exploitation_stats()}) + return jsonify( + {"report": None, "stats": RansomwareReportService.get_exploitation_details()} + ) diff --git a/monkey/monkey_island/cc/services/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware_report.py index 93def94d7..fd1ec5b69 100644 --- a/monkey/monkey_island/cc/services/ransomware_report.py +++ b/monkey/monkey_island/cc/services/ransomware_report.py @@ -6,7 +6,7 @@ class RansomwareReportService: pass @staticmethod - def get_exploitation_stats(): + def get_exploitation_details(): scanned = ReportService.get_scanned() exploited = ReportService.get_exploited() From 27058cc827c8503cc2b6c239e5c62abd01cd4c7f Mon Sep 17 00:00:00 2001 From: Shreya Date: Thu, 8 Jul 2021 19:49:08 +0530 Subject: [PATCH 03/11] island: Remove unnecessary code in RansomwareReportService --- monkey/monkey_island/cc/services/ransomware_report.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/monkey/monkey_island/cc/services/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware_report.py index fd1ec5b69..23fb8cec4 100644 --- a/monkey/monkey_island/cc/services/ransomware_report.py +++ b/monkey/monkey_island/cc/services/ransomware_report.py @@ -2,9 +2,6 @@ from monkey_island.cc.services.reporting.report import ReportService class RansomwareReportService: - def __init__(self): - pass - @staticmethod def get_exploitation_details(): scanned = ReportService.get_scanned() From 38bead54ae5fde912a32ff99ef1f8f4e2050d073 Mon Sep 17 00:00:00 2001 From: Shreya Date: Thu, 8 Jul 2021 19:53:52 +0530 Subject: [PATCH 04/11] island: Extract methods (all static) in class RansomwareReportService and remove the class --- monkey/monkey_island/cc/resources/ransomware_report.py | 6 ++---- monkey/monkey_island/cc/services/ransomware_report.py | 10 ++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/monkey/monkey_island/cc/resources/ransomware_report.py b/monkey/monkey_island/cc/resources/ransomware_report.py index 96abfe252..d13a216e9 100644 --- a/monkey/monkey_island/cc/resources/ransomware_report.py +++ b/monkey/monkey_island/cc/resources/ransomware_report.py @@ -2,12 +2,10 @@ import flask_restful from flask import jsonify from monkey_island.cc.resources.auth.auth import jwt_required -from monkey_island.cc.services.ransomware_report import RansomwareReportService +from monkey_island.cc.services.ransomware_report import get_exploitation_details class RansomwareReport(flask_restful.Resource): @jwt_required def get(self): - return jsonify( - {"report": None, "stats": RansomwareReportService.get_exploitation_details()} - ) + return jsonify({"report": None, "stats": get_exploitation_details()}) diff --git a/monkey/monkey_island/cc/services/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware_report.py index 23fb8cec4..74d30cf93 100644 --- a/monkey/monkey_island/cc/services/ransomware_report.py +++ b/monkey/monkey_island/cc/services/ransomware_report.py @@ -1,10 +1,8 @@ from monkey_island.cc.services.reporting.report import ReportService -class RansomwareReportService: - @staticmethod - def get_exploitation_details(): - scanned = ReportService.get_scanned() - exploited = ReportService.get_exploited() +def get_exploitation_details(): + scanned = ReportService.get_scanned() + exploited = ReportService.get_exploited() - return {"scanned": scanned, "exploited": exploited} + return {"scanned": scanned, "exploited": exploited} From a95adfb5b6a2fbe0174c333ba6537ac88133f62e Mon Sep 17 00:00:00 2001 From: Shreya Date: Thu, 8 Jul 2021 19:59:34 +0530 Subject: [PATCH 05/11] island: Replace key ("stats" -> "propagation") in RansomwareReport data --- monkey/monkey_island/cc/resources/ransomware_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/resources/ransomware_report.py b/monkey/monkey_island/cc/resources/ransomware_report.py index d13a216e9..c5dfef3a8 100644 --- a/monkey/monkey_island/cc/resources/ransomware_report.py +++ b/monkey/monkey_island/cc/resources/ransomware_report.py @@ -8,4 +8,4 @@ from monkey_island.cc.services.ransomware_report import get_exploitation_details class RansomwareReport(flask_restful.Resource): @jwt_required def get(self): - return jsonify({"report": None, "stats": get_exploitation_details()}) + return jsonify({"report": None, "propagation": get_exploitation_details()}) From 2212029f0bd07648a0eab5ae44d706e3fb869f39 Mon Sep 17 00:00:00 2001 From: Shreya Date: Mon, 12 Jul 2021 13:11:44 +0530 Subject: [PATCH 06/11] cc: Process exploit data on backend for ransomware stats reporting --- .../cc/resources/ransomware_report.py | 4 ++-- .../cc/services/ransomware_report.py | 24 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/resources/ransomware_report.py b/monkey/monkey_island/cc/resources/ransomware_report.py index c5dfef3a8..93a318114 100644 --- a/monkey/monkey_island/cc/resources/ransomware_report.py +++ b/monkey/monkey_island/cc/resources/ransomware_report.py @@ -2,10 +2,10 @@ import flask_restful from flask import jsonify from monkey_island.cc.resources.auth.auth import jwt_required -from monkey_island.cc.services.ransomware_report import get_exploitation_details +from monkey_island.cc.services.ransomware_report import get_propagation_stats class RansomwareReport(flask_restful.Resource): @jwt_required def get(self): - return jsonify({"report": None, "propagation": get_exploitation_details()}) + return jsonify({"report": None, "propagation_stats": get_propagation_stats()}) diff --git a/monkey/monkey_island/cc/services/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware_report.py index 74d30cf93..691ae1fd3 100644 --- a/monkey/monkey_island/cc/services/ransomware_report.py +++ b/monkey/monkey_island/cc/services/ransomware_report.py @@ -1,8 +1,28 @@ +from typing import Dict, List + from monkey_island.cc.services.reporting.report import ReportService -def get_exploitation_details(): +def get_propagation_stats() -> Dict: scanned = ReportService.get_scanned() exploited = ReportService.get_exploited() - return {"scanned": scanned, "exploited": exploited} + return { + "num_scanned_nodes": len(scanned), + "num_exploited_nodes": len(exploited), + "count_per_exploit": _get_exploit_counts(exploited), + } + + +def _get_exploit_counts(exploited: List[Dict]) -> Dict: + exploit_counts = {} + + for node in exploited: + exploits = node["exploits"] + for exploit in exploits: + if exploit in exploit_counts: + exploit_counts[exploit] += 1 + else: + exploit_counts[exploit] = 1 + + return exploit_counts From 4564596cd0d8d0b1ba576407d8afeb12926850b3 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 12 Jul 2021 11:48:16 -0400 Subject: [PATCH 07/11] Agent: Add unit tests for ransomware report service --- .../cc/services/test_ransomware_report.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py new file mode 100644 index 000000000..6997d586c --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py @@ -0,0 +1,37 @@ +import pytest + +import monkey_island.cc.services.ransomware_report as ransomware_report +from monkey_island.cc.services.reporting.report import ReportService + +TEST_SCANNED_RESULTS = [{}, {}, {}, {}] +TEST_EXPLOITED_RESULTS = [ + {"exploits": ["SSH Exploiter"]}, + {"exploits": ["SSH Exploiter", "SMB Exploiter"]}, + {"exploits": ["WMI Exploiter"]}, +] + + +@pytest.fixture(scope="function", autouse=True) +def patch_report_service(monkeypatch): + monkeypatch.setattr(ReportService, "get_scanned", lambda: TEST_SCANNED_RESULTS) + monkeypatch.setattr(ReportService, "get_exploited", lambda: TEST_EXPLOITED_RESULTS) + + +def test_get_propagation_stats__num_scanned(): + stats = ransomware_report.get_propagation_stats() + + assert stats["num_scanned_nodes"] == 4 + + +def test_get_propagation_stats__num_exploited(): + stats = ransomware_report.get_propagation_stats() + + assert stats["num_exploited_nodes"] == 3 + + +def test_get_propagation_stats__count_per_exploit(): + stats = ransomware_report.get_propagation_stats() + + assert stats["count_per_exploit"]["SSH Exploiter"] == 2 + assert stats["count_per_exploit"]["SMB Exploiter"] == 1 + assert stats["count_per_exploit"]["WMI Exploiter"] == 1 From 06439d92f9d1e0e172a1f3b6ce5e9cb16c259942 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 12 Jul 2021 11:50:39 -0400 Subject: [PATCH 08/11] Island: Rename count_per_exploit -> num_exploited_per_exploit --- monkey/monkey_island/cc/services/ransomware_report.py | 2 +- .../monkey_island/cc/services/test_ransomware_report.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/services/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware_report.py index 691ae1fd3..65098daec 100644 --- a/monkey/monkey_island/cc/services/ransomware_report.py +++ b/monkey/monkey_island/cc/services/ransomware_report.py @@ -10,7 +10,7 @@ def get_propagation_stats() -> Dict: return { "num_scanned_nodes": len(scanned), "num_exploited_nodes": len(exploited), - "count_per_exploit": _get_exploit_counts(exploited), + "num_exploited_per_exploit": _get_exploit_counts(exploited), } diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py b/monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py index 6997d586c..5894d70ca 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py @@ -29,9 +29,9 @@ def test_get_propagation_stats__num_exploited(): assert stats["num_exploited_nodes"] == 3 -def test_get_propagation_stats__count_per_exploit(): +def test_get_propagation_stats__num_exploited_per_exploit(): stats = ransomware_report.get_propagation_stats() - assert stats["count_per_exploit"]["SSH Exploiter"] == 2 - assert stats["count_per_exploit"]["SMB Exploiter"] == 1 - assert stats["count_per_exploit"]["WMI Exploiter"] == 1 + assert stats["num_exploited_per_exploit"]["SSH Exploiter"] == 2 + assert stats["num_exploited_per_exploit"]["SMB Exploiter"] == 1 + assert stats["num_exploited_per_exploit"]["WMI Exploiter"] == 1 From 9e7e58658c8cfcf2afc8d25240d885a6d198db56 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 12 Jul 2021 11:54:39 -0400 Subject: [PATCH 09/11] Island: Simplify _get_exploit_counts() --- monkey/monkey_island/cc/services/ransomware_report.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/monkey/monkey_island/cc/services/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware_report.py index 65098daec..0e0f1c299 100644 --- a/monkey/monkey_island/cc/services/ransomware_report.py +++ b/monkey/monkey_island/cc/services/ransomware_report.py @@ -18,11 +18,7 @@ def _get_exploit_counts(exploited: List[Dict]) -> Dict: exploit_counts = {} for node in exploited: - exploits = node["exploits"] - for exploit in exploits: - if exploit in exploit_counts: - exploit_counts[exploit] += 1 - else: - exploit_counts[exploit] = 1 + for exploit in node["exploits"]: + exploit_counts[exploit] = exploit_counts.get(exploit, 0) + 1 return exploit_counts From e4cd06d8c309c1b6dd0109813be0349b5171069c Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 12 Jul 2021 12:00:14 -0400 Subject: [PATCH 10/11] Island: Move ransomware_report.py to ransomware/ransomware_report.py --- monkey/monkey_island/cc/resources/ransomware_report.py | 6 ++++-- .../cc/services/{ => ransomware}/ransomware_report.py | 0 .../cc/services/{ => ransomware}/test_ransomware_report.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) rename monkey/monkey_island/cc/services/{ => ransomware}/ransomware_report.py (100%) rename monkey/tests/unit_tests/monkey_island/cc/services/{ => ransomware}/test_ransomware_report.py (94%) diff --git a/monkey/monkey_island/cc/resources/ransomware_report.py b/monkey/monkey_island/cc/resources/ransomware_report.py index 93a318114..cb5923468 100644 --- a/monkey/monkey_island/cc/resources/ransomware_report.py +++ b/monkey/monkey_island/cc/resources/ransomware_report.py @@ -2,10 +2,12 @@ import flask_restful from flask import jsonify from monkey_island.cc.resources.auth.auth import jwt_required -from monkey_island.cc.services.ransomware_report import get_propagation_stats +from monkey_island.cc.services.ransomware import ransomware_report class RansomwareReport(flask_restful.Resource): @jwt_required def get(self): - return jsonify({"report": None, "propagation_stats": get_propagation_stats()}) + return jsonify( + {"report": None, "propagation_stats": ransomware_report.get_propagation_stats()} + ) diff --git a/monkey/monkey_island/cc/services/ransomware_report.py b/monkey/monkey_island/cc/services/ransomware/ransomware_report.py similarity index 100% rename from monkey/monkey_island/cc/services/ransomware_report.py rename to monkey/monkey_island/cc/services/ransomware/ransomware_report.py diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py b/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py similarity index 94% rename from monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py rename to monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py index 5894d70ca..4b3493e82 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/test_ransomware_report.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py @@ -1,6 +1,6 @@ import pytest -import monkey_island.cc.services.ransomware_report as ransomware_report +from monkey_island.cc.services.ransomware import ransomware_report from monkey_island.cc.services.reporting.report import ReportService TEST_SCANNED_RESULTS = [{}, {}, {}, {}] From c7d655ac7dd392ba4a568c9a5ff9ed56b68f9fab Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Mon, 12 Jul 2021 12:08:31 -0400 Subject: [PATCH 11/11] Tests: Set autouse=False for patch_report_service fixture --- .../ransomware/test_ransomware_report.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py b/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py index 4b3493e82..4c586aa81 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/ransomware/test_ransomware_report.py @@ -3,33 +3,33 @@ import pytest from monkey_island.cc.services.ransomware import ransomware_report from monkey_island.cc.services.reporting.report import ReportService -TEST_SCANNED_RESULTS = [{}, {}, {}, {}] -TEST_EXPLOITED_RESULTS = [ - {"exploits": ["SSH Exploiter"]}, - {"exploits": ["SSH Exploiter", "SMB Exploiter"]}, - {"exploits": ["WMI Exploiter"]}, -] +@pytest.fixture +def patch_report_service_for_stats(monkeypatch): + TEST_SCANNED_RESULTS = [{}, {}, {}, {}] + TEST_EXPLOITED_RESULTS = [ + {"exploits": ["SSH Exploiter"]}, + {"exploits": ["SSH Exploiter", "SMB Exploiter"]}, + {"exploits": ["WMI Exploiter"]}, + ] -@pytest.fixture(scope="function", autouse=True) -def patch_report_service(monkeypatch): monkeypatch.setattr(ReportService, "get_scanned", lambda: TEST_SCANNED_RESULTS) monkeypatch.setattr(ReportService, "get_exploited", lambda: TEST_EXPLOITED_RESULTS) -def test_get_propagation_stats__num_scanned(): +def test_get_propagation_stats__num_scanned(patch_report_service_for_stats): stats = ransomware_report.get_propagation_stats() assert stats["num_scanned_nodes"] == 4 -def test_get_propagation_stats__num_exploited(): +def test_get_propagation_stats__num_exploited(patch_report_service_for_stats): stats = ransomware_report.get_propagation_stats() assert stats["num_exploited_nodes"] == 3 -def test_get_propagation_stats__num_exploited_per_exploit(): +def test_get_propagation_stats__num_exploited_per_exploit(patch_report_service_for_stats): stats = ransomware_report.get_propagation_stats() assert stats["num_exploited_per_exploit"]["SSH Exploiter"] == 2