From 026cb41064150191609b05805dd6d24a06a13b3c Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Thu, 30 May 2019 09:17:24 +0300 Subject: [PATCH 1/4] WIP - initial code, just trying to make a POST request work --- monkey/monkey_island/cc/resources/netmap.py | 31 +++++++++++++++++++ .../cc/ui/src/components/pages/ReportPage.js | 12 ++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/resources/netmap.py b/monkey/monkey_island/cc/resources/netmap.py index ed83414f5..96ea38b28 100644 --- a/monkey/monkey_island/cc/resources/netmap.py +++ b/monkey/monkey_island/cc/resources/netmap.py @@ -1,4 +1,7 @@ +import json + import flask_restful +from flask import request from monkey_island.cc.auth import jwt_required from monkey_island.cc.services.edge import EdgeService @@ -28,4 +31,32 @@ class NetMap(flask_restful.Resource): "edges": edges } + @jwt_required() + def post(self, **kw): + post_data = json.loads(request.data) + print(post_data) + + monkeys = [NodeService.monkey_to_net_node(x) for x in mongo.db.monkey.find({})] + nodes = [NodeService.node_to_net_node(x) for x in mongo.db.node.find({})] + edges = [EdgeService.edge_to_net_edge(x) for x in mongo.db.edge.find({})] + + if NodeService.get_monkey_island_monkey() is None: + monkey_island = [NodeService.get_monkey_island_pseudo_net_node()] + edges += EdgeService.get_monkey_island_pseudo_edges() + else: + monkey_island = [] + edges += EdgeService.get_infected_monkey_island_pseudo_edges() + + all_nodes = monkeys + nodes + monkey_island + def filter_linux(machine): + if machine["os"] == "linux": + return False + return True + all_nodes = filter(filter_linux, all_nodes) + + return \ + { + "nodes": all_nodes, + "edges": edges + } 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 eb94792ab..65a8b9003 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -103,7 +103,17 @@ class ReportPageComponent extends AuthComponent { }; updateMapFromServer = () => { - this.authFetch('/api/netmap') + this.authFetch('/api/netmap', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + firstParam: 'yourValue', + secondParam: 'yourOtherValue', + }) + }) .then(res => res.json()) .then(res => { res.edges.forEach(edge => { From bf0b812f7330f8ae4f518d2de720e7598e419c63 Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 5 Jun 2019 17:18:52 +0300 Subject: [PATCH 2/4] Revert "WIP - initial code, just trying to make a POST request work" This reverts commit 026cb41064150191609b05805dd6d24a06a13b3c. --- monkey/monkey_island/cc/resources/netmap.py | 31 ------------------- .../cc/ui/src/components/pages/ReportPage.js | 12 +------ 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/monkey/monkey_island/cc/resources/netmap.py b/monkey/monkey_island/cc/resources/netmap.py index 96ea38b28..ed83414f5 100644 --- a/monkey/monkey_island/cc/resources/netmap.py +++ b/monkey/monkey_island/cc/resources/netmap.py @@ -1,7 +1,4 @@ -import json - import flask_restful -from flask import request from monkey_island.cc.auth import jwt_required from monkey_island.cc.services.edge import EdgeService @@ -31,32 +28,4 @@ class NetMap(flask_restful.Resource): "edges": edges } - @jwt_required() - def post(self, **kw): - post_data = json.loads(request.data) - print(post_data) - - monkeys = [NodeService.monkey_to_net_node(x) for x in mongo.db.monkey.find({})] - nodes = [NodeService.node_to_net_node(x) for x in mongo.db.node.find({})] - edges = [EdgeService.edge_to_net_edge(x) for x in mongo.db.edge.find({})] - - if NodeService.get_monkey_island_monkey() is None: - monkey_island = [NodeService.get_monkey_island_pseudo_net_node()] - edges += EdgeService.get_monkey_island_pseudo_edges() - else: - monkey_island = [] - edges += EdgeService.get_infected_monkey_island_pseudo_edges() - - all_nodes = monkeys + nodes + monkey_island - def filter_linux(machine): - if machine["os"] == "linux": - return False - return True - all_nodes = filter(filter_linux, all_nodes) - - return \ - { - "nodes": all_nodes, - "edges": edges - } 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 65a8b9003..eb94792ab 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -103,17 +103,7 @@ class ReportPageComponent extends AuthComponent { }; updateMapFromServer = () => { - this.authFetch('/api/netmap', { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - firstParam: 'yourValue', - secondParam: 'yourOtherValue', - }) - }) + this.authFetch('/api/netmap') .then(res => res.json()) .then(res => { res.edges.forEach(edge => { From 833af00421ea91b168cdb931564b79375b5cabcc Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 5 Jun 2019 18:28:00 +0300 Subject: [PATCH 3/4] Added the IslandTestCase class for better testing + moved get_os to the monkey model --- monkey/monkey_island/cc/models/monkey.py | 8 +++++ monkey/monkey_island/cc/models/test_monkey.py | 29 +++++++++++++++++-- .../cc/testing/IslandTestCase.py | 12 ++++++++ monkey/monkey_island/cc/testing/__init__.py | 0 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 monkey/monkey_island/cc/testing/IslandTestCase.py create mode 100644 monkey/monkey_island/cc/testing/__init__.py diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index bb018caa9..3e1e3d7c5 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -55,6 +55,14 @@ class Monkey(Document): monkey_is_dead = True return monkey_is_dead + def get_os(self): + os = "unknown" + if self.description.lower().find("linux") != -1: + os = "linux" + elif self.description.lower().find("windows") != -1: + os = "windows" + return os + class MonkeyNotFoundError(Exception): pass diff --git a/monkey/monkey_island/cc/models/test_monkey.py b/monkey/monkey_island/cc/models/test_monkey.py index 008fb0ce6..a744db6b6 100644 --- a/monkey/monkey_island/cc/models/test_monkey.py +++ b/monkey/monkey_island/cc/models/test_monkey.py @@ -1,13 +1,13 @@ import uuid from time import sleep -from unittest import TestCase from monkey import Monkey from monkey_island.cc.models.monkey import MonkeyNotFoundError +from monkey_island.cc.testing.IslandTestCase import IslandTestCase from monkey_ttl import MonkeyTtl -class TestMonkey(TestCase): +class TestMonkey(IslandTestCase): """ Make sure to set server environment to `testing` in server.json! Otherwise this will mess up your mongo instance and won't work. @@ -15,7 +15,11 @@ class TestMonkey(TestCase): Also, the working directory needs to be the working directory from which you usually run the island so the server.json file is found and loaded. """ + def test_is_dead(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + # Arrange alive_monkey_ttl = MonkeyTtl.create_ttl_expire_in(30) alive_monkey_ttl.save() @@ -43,6 +47,9 @@ class TestMonkey(TestCase): self.assertFalse(alive_monkey.is_dead()) def test_get_single_monkey_by_id(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + # Arrange a_monkey = Monkey(guid=str(uuid.uuid4())) a_monkey.save() @@ -52,3 +59,21 @@ class TestMonkey(TestCase): self.assertIsNotNone(Monkey.get_single_monkey_by_id(a_monkey.id)) # Raise on non-existent monkey self.assertRaises(MonkeyNotFoundError, Monkey.get_single_monkey_by_id, "abcdefabcdefabcdefabcdef") + + def test_get_os(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + linux_monkey = Monkey(guid=str(uuid.uuid4()), + description="Linux shay-Virtual-Machine 4.15.0-50-generic #54-Ubuntu SMP Mon May 6 18:46:08 UTC 2019 x86_64 x86_64") + windows_monkey = Monkey(guid=str(uuid.uuid4()), + description="Windows bla bla bla") + unknown_monkey = Monkey(guid=str(uuid.uuid4()), + description="bla bla bla") + linux_monkey.save() + windows_monkey.save() + unknown_monkey.save() + + self.assertEquals(1, len(filter(lambda m: m.get_os() == "windows", Monkey.objects()))) + self.assertEquals(1, len(filter(lambda m: m.get_os() == "linux", Monkey.objects()))) + self.assertEquals(1, len(filter(lambda m: m.get_os() == "unknown", Monkey.objects()))) diff --git a/monkey/monkey_island/cc/testing/IslandTestCase.py b/monkey/monkey_island/cc/testing/IslandTestCase.py new file mode 100644 index 000000000..e894f13df --- /dev/null +++ b/monkey/monkey_island/cc/testing/IslandTestCase.py @@ -0,0 +1,12 @@ +import unittest +from monkey_island.cc.environment.environment import env +from monkey_island.cc.models import Monkey + + +class IslandTestCase(unittest.TestCase): + def fail_if_not_testing_env(self): + self.failIf(not env.testing, "Change server_config.json to testing environment.") + + @staticmethod + def clean_monkey_db(): + Monkey.objects().delete() diff --git a/monkey/monkey_island/cc/testing/__init__.py b/monkey/monkey_island/cc/testing/__init__.py new file mode 100644 index 000000000..e69de29bb From 712ce4622d145c47fc3c38e1527f03dc1ab6fa6e Mon Sep 17 00:00:00 2001 From: Shay Nehmad Date: Wed, 5 Jun 2019 18:28:54 +0300 Subject: [PATCH 4/4] Refactored the GenerateMapNodes function to use the DAL and now it filters windows nodes --- .../monkey_island/cc/services/pth_report.py | 13 ++-- .../cc/services/test_PTHReportService.py | 69 +++++++++++++++++++ 2 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 monkey/monkey_island/cc/services/test_PTHReportService.py diff --git a/monkey/monkey_island/cc/services/pth_report.py b/monkey/monkey_island/cc/services/pth_report.py index 93fd51989..9f3b9769f 100644 --- a/monkey/monkey_island/cc/services/pth_report.py +++ b/monkey/monkey_island/cc/services/pth_report.py @@ -1,6 +1,7 @@ from itertools import product from monkey_island.cc.database import mongo +from monkey_island.cc.models import Monkey from bson import ObjectId from monkey_island.cc.services.groups_and_users_consts import USERTYPE @@ -216,15 +217,15 @@ class PTHReportService(object): @staticmethod def generate_map_nodes(): - monkeys = mongo.db.monkey.find({}, {'_id': 1, 'hostname': 1, 'critical_services': 1, 'ip_addresses': 1}) + monkeys = filter(lambda m: m.get_os() == "windows", Monkey.objects()) return [ { - 'id': monkey['_id'], - 'label': '{0} : {1}'.format(monkey['hostname'], monkey['ip_addresses'][0]), - 'group': 'critical' if monkey.get('critical_services', []) else 'normal', - 'services': monkey.get('critical_services', []), - 'hostname': monkey['hostname'] + 'id': monkey.guid, + 'label': '{0} : {1}'.format(monkey.hostname, monkey.ip_addresses[0]), + 'group': 'critical' if monkey.critical_services is not None else 'normal', + 'services': monkey.critical_services, + 'hostname': monkey.hostname } for monkey in monkeys ] diff --git a/monkey/monkey_island/cc/services/test_PTHReportService.py b/monkey/monkey_island/cc/services/test_PTHReportService.py new file mode 100644 index 000000000..24e560ff0 --- /dev/null +++ b/monkey/monkey_island/cc/services/test_PTHReportService.py @@ -0,0 +1,69 @@ +import uuid + +from monkey_island.cc.models import Monkey +from monkey_island.cc.services.pth_report import PTHReportService +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + + +class TestPTHReportServiceGenerateMapNodes(IslandTestCase): + def test_generate_map_nodes(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + self.assertEqual(PTHReportService.generate_map_nodes(), []) + + windows_monkey_with_services = Monkey( + guid=str(uuid.uuid4()), + hostname="A_Windows_PC_1", + critical_services=["aCriticalService", "Domain Controller"], + ip_addresses=["1.1.1.1", "2.2.2.2"], + description="windows 10" + ) + windows_monkey_with_services.save() + + windows_monkey_with_no_services = Monkey( + guid=str(uuid.uuid4()), + hostname="A_Windows_PC_2", + critical_services=[], + ip_addresses=["3.3.3.3"], + description="windows 10" + ) + windows_monkey_with_no_services.save() + + linux_monkey = Monkey( + guid=str(uuid.uuid4()), + hostname="A_Linux_PC", + ip_addresses=["4.4.4.4"], + description="linux ubuntu" + ) + linux_monkey.save() + + map_nodes = PTHReportService.generate_map_nodes() + + self.assertEquals(2, len(map_nodes)) + + def test_generate_map_nodes_parsing(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + monkey_id = str(uuid.uuid4()) + hostname = "A_Windows_PC_1" + windows_monkey_with_services = Monkey( + guid=monkey_id, + hostname=hostname, + critical_services=["aCriticalService", "Domain Controller"], + ip_addresses=["1.1.1.1"], + description="windows 10" + ) + windows_monkey_with_services.save() + + map_nodes = PTHReportService.generate_map_nodes() + + self.assertEquals(map_nodes[0]["id"], monkey_id) + self.assertEquals(map_nodes[0]["label"], "A_Windows_PC_1 : 1.1.1.1") + self.assertEquals(map_nodes[0]["group"], "critical") + self.assertEquals(len(map_nodes[0]["services"]), 2) + self.assertEquals(map_nodes[0]["hostname"], hostname) + + +