diff --git a/.github/attack-report.png b/.github/attack-report.png
new file mode 100644
index 00000000000..5ba45e9fb89
Binary files /dev/null and b/.github/attack-report.png differ
diff --git a/.github/map-full.png b/.github/map-full.png
index faa1b7832fc..92902a22132 100644
Binary files a/.github/map-full.png and b/.github/map-full.png differ
diff --git a/.github/security-report.png b/.github/security-report.png
new file mode 100644
index 00000000000..54a30b13f13
Binary files /dev/null and b/.github/security-report.png differ
diff --git a/.github/zero-trust-report.png b/.github/zero-trust-report.png
new file mode 100644
index 00000000000..18a0ff7039a
Binary files /dev/null and b/.github/zero-trust-report.png differ
diff --git a/.gitmodules b/.gitmodules
index 92a84cb37ce..63b69ebab1f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
+
[submodule "monkey/monkey_island/cc/services/attack/attack_data"]
path = monkey/monkey_island/cc/services/attack/attack_data
- url = https://github.com/mitre/cti
+ url = https://github.com/guardicore/cti
diff --git a/README.md b/README.md
index 83fd878adbf..bf976845938 100644
--- a/README.md
+++ b/README.md
@@ -13,10 +13,6 @@ Welcome to the Infection Monkey!
The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Monkey uses various methods to self propagate across a data center and reports success to a centralized Monkey Island server.
-
-
-
-
The Infection Monkey is comprised of two parts:
* **Monkey** - A tool which infects other machines and propagates to them.
@@ -24,6 +20,20 @@ The Infection Monkey is comprised of two parts:
To read more about the Monkey, visit [infectionmonkey.com](https://infectionmonkey.com).
+## Screenshots
+
+### Map
+
+
+### Security report
+
+
+### Zero trust report
+
+
+### ATT&CK report
+
+
## Main Features
The Infection Monkey uses the following techniques and exploits to propagate to other machines.
@@ -40,6 +50,8 @@ The Infection Monkey uses the following techniques and exploits to propagate to
* Conficker
* SambaCry
* Elastic Search (CVE-2015-1427)
+ * Weblogic server
+ * and more
## Setup
Check out the [Setup](https://github.com/guardicore/monkey/wiki/setup) page in the Wiki or a quick getting [started guide](https://www.guardicore.com/infectionmonkey/wt/).
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 2d0d0b55b6a..aec69fd32e3 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -2,7 +2,7 @@ FROM debian:stretch-slim
LABEL MAINTAINER="theonlydoo "
-ARG RELEASE=1.6
+ARG RELEASE=1.8.0
ARG DEBIAN_FRONTEND=noninteractive
EXPOSE 5000
diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py
index 3e1c4819943..d9067eeeec3 100644
--- a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py
+++ b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py
@@ -1,56 +1,36 @@
import logging
from datetime import timedelta
+from typing import Dict
from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer
-from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
-
-MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2)
-MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5)
-
-REPORT_URLS = [
- "api/report/security",
- "api/attack/report",
- "api/report/zero_trust/findings",
- "api/report/zero_trust/principles",
- "api/report/zero_trust/pillars"
-]
-
-logger = logging.getLogger(__name__)
+from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
+LOGGER = logging.getLogger(__name__)
class PerformanceAnalyzer(Analyzer):
- def __init__(self, island_client: MonkeyIslandClient, break_if_took_too_long=False):
- self.break_if_took_too_long = break_if_took_too_long
- self.island_client = island_client
-
- def analyze_test_results(self) -> bool:
- if not self.island_client.is_all_monkeys_dead():
- raise RuntimeError("Can't test report times since not all Monkeys have died.")
-
- # Collect timings for all pages
- self.island_client.clear_caches()
- report_resource_to_response_time = {}
- for url in REPORT_URLS:
- report_resource_to_response_time[url] = self.island_client.get_elapsed_for_get_request(url)
+ def __init__(self, performance_test_config: PerformanceTestConfig, endpoint_timings: Dict[str, timedelta]):
+ self.performance_test_config = performance_test_config
+ self.endpoint_timings = endpoint_timings
+ def analyze_test_results(self):
# Calculate total time and check each page
single_page_time_less_then_max = True
total_time = timedelta()
- for page, elapsed in report_resource_to_response_time.items():
- logger.info(f"page {page} took {str(elapsed)}")
+ for page, elapsed in self.endpoint_timings.items():
+ LOGGER.info(f"page {page} took {str(elapsed)}")
total_time += elapsed
- if elapsed > MAX_ALLOWED_SINGLE_PAGE_TIME:
+ if elapsed > self.performance_test_config.max_allowed_single_page_time:
single_page_time_less_then_max = False
- total_time_less_then_max = total_time < MAX_ALLOWED_TOTAL_TIME
+ total_time_less_then_max = total_time < self.performance_test_config.max_allowed_total_time
- logger.info(f"total time is {str(total_time)}")
+ LOGGER.info(f"total time is {str(total_time)}")
performance_is_good_enough = total_time_less_then_max and single_page_time_less_then_max
- if self.break_if_took_too_long and not performance_is_good_enough:
- logger.warning(
+ if self.performance_test_config.break_on_timeout and not performance_is_good_enough:
+ LOGGER.warning(
"Calling breakpoint - pausing to enable investigation of island. Type 'c' to continue once you're done "
"investigating. Type 'p timings' and 'p total_time' to see performance information."
)
diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py
index 27a54222b79..93780bf3b9f 100644
--- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py
+++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py
@@ -96,13 +96,3 @@ def clear_caches(self):
response = self.requests.get("api/test/clear_caches")
response.raise_for_status()
return response
-
- def get_elapsed_for_get_request(self, url):
- response = self.requests.get(url)
- if response.ok:
- LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120].strip()}")
- return response.elapsed
- else:
- LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}")
- # instead of raising for status, mark failed responses as maxtime
- return timedelta.max()
diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py
index 7ac70d8e016..04e510c55d3 100644
--- a/envs/monkey_zoo/blackbox/test_blackbox.py
+++ b/envs/monkey_zoo/blackbox/test_blackbox.py
@@ -4,12 +4,13 @@
import pytest
from time import sleep
-from envs.monkey_zoo.blackbox.analyzers.performance_analyzer import PerformanceAnalyzer
from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer
from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser
+from envs.monkey_zoo.blackbox.tests.performance.map_generation import MapGenerationTest
+from envs.monkey_zoo.blackbox.tests.performance.report_generation import ReportGenerationTest
from envs.monkey_zoo.blackbox.utils import gcp_machine_handlers
-from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
+from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler
DEFAULT_TIMEOUT_SECONDS = 5*60
@@ -55,34 +56,32 @@ def island_client(island):
class TestMonkeyBlackbox(object):
@staticmethod
- def run_basic_test(island_client, conf_filename, test_name, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS):
+ def run_exploitation_test(island_client, conf_filename, test_name, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS):
config_parser = IslandConfigParser(conf_filename)
analyzer = CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets())
log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path())
- BasicTest(
+ ExploitationTest(
name=test_name,
island_client=island_client,
config_parser=config_parser,
analyzers=[analyzer],
timeout=timeout_in_seconds,
- post_exec_analyzers=[],
log_handler=log_handler).run()
@staticmethod
- def run_performance_test(island_client, conf_filename, test_name, timeout_in_seconds):
+ def run_performance_test(performance_test_class, island_client,
+ conf_filename, timeout_in_seconds, break_on_timeout=False):
config_parser = IslandConfigParser(conf_filename)
- log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path())
- BasicTest(
- name=test_name,
- island_client=island_client,
- config_parser=config_parser,
- analyzers=[CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets())],
- timeout=timeout_in_seconds,
- post_exec_analyzers=[PerformanceAnalyzer(
- island_client,
- break_if_took_too_long=False
- )],
- log_handler=log_handler).run()
+ log_handler = TestLogsHandler(performance_test_class.TEST_NAME,
+ island_client,
+ TestMonkeyBlackbox.get_log_dir_path())
+ analyzers = [CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets())]
+ performance_test_class(island_client=island_client,
+ config_parser=config_parser,
+ analyzers=analyzers,
+ timeout=timeout_in_seconds,
+ log_handler=log_handler,
+ break_on_timeout=break_on_timeout).run()
@staticmethod
def get_log_dir_path():
@@ -92,43 +91,42 @@ def test_server_online(self, island_client):
assert island_client.get_api_status() is not None
def test_ssh_exploiter(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "SSH.conf", "SSH_exploiter_and_keys")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "SSH.conf", "SSH_exploiter_and_keys")
def test_hadoop_exploiter(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "HADOOP.conf", "Hadoop_exploiter", 6*60)
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "HADOOP.conf", "Hadoop_exploiter", 6 * 60)
def test_mssql_exploiter(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "MSSQL.conf", "MSSQL_exploiter")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "MSSQL.conf", "MSSQL_exploiter")
def test_smb_and_mimikatz_exploiters(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "SMB_MIMIKATZ.conf", "SMB_exploiter_mimikatz")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "SMB_MIMIKATZ.conf", "SMB_exploiter_mimikatz")
def test_smb_pth(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "SMB_PTH.conf", "SMB_PTH")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "SMB_PTH.conf", "SMB_PTH")
def test_elastic_exploiter(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "ELASTIC.conf", "Elastic_exploiter")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "ELASTIC.conf", "Elastic_exploiter")
def test_struts_exploiter(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "STRUTS2.conf", "Strtuts2_exploiter")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "STRUTS2.conf", "Strtuts2_exploiter")
def test_weblogic_exploiter(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "WEBLOGIC.conf", "Weblogic_exploiter")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "WEBLOGIC.conf", "Weblogic_exploiter")
def test_shellshock_exploiter(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "SHELLSHOCK.conf", "Shellschock_exploiter")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "SHELLSHOCK.conf", "Shellschock_exploiter")
def test_tunneling(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "TUNNELING.conf", "Tunneling_exploiter", 15*60)
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "TUNNELING.conf", "Tunneling_exploiter", 15 * 60)
def test_wmi_and_mimikatz_exploiters(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "WMI_MIMIKATZ.conf", "WMI_exploiter,_mimikatz")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "WMI_MIMIKATZ.conf", "WMI_exploiter,_mimikatz")
def test_wmi_pth(self, island_client):
- TestMonkeyBlackbox.run_basic_test(island_client, "WMI_PTH.conf", "WMI_PTH")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, "WMI_PTH.conf", "WMI_PTH")
- @pytest.mark.xfail(reason="Performance is slow, will improve on release 1.9.")
- def test_performance(self, island_client):
+ def test_report_generation_performance(self, island_client):
"""
This test includes the SSH + Elastic + Hadoop + MSSQL machines all in one test
for a total of 8 machines including the Monkey Island.
@@ -136,8 +134,14 @@ def test_performance(self, island_client):
Is has 2 analyzers - the regular one which checks all the Monkeys
and the Timing one which checks how long the report took to execute
"""
- TestMonkeyBlackbox.run_performance_test(
- island_client,
- "PERFORMANCE.conf",
- "test_report_performance",
- timeout_in_seconds=10*60)
+ TestMonkeyBlackbox.run_performance_test(ReportGenerationTest,
+ island_client,
+ "PERFORMANCE.conf",
+ timeout_in_seconds=10*60)
+
+ def test_map_generation_performance(self, island_client):
+ TestMonkeyBlackbox.run_performance_test(MapGenerationTest,
+ island_client,
+ "PERFORMANCE.conf",
+ timeout_in_seconds=10*60)
+
diff --git a/envs/monkey_zoo/blackbox/tests/basic_test.py b/envs/monkey_zoo/blackbox/tests/basic_test.py
index a5e71c64c46..fa722ffb759 100644
--- a/envs/monkey_zoo/blackbox/tests/basic_test.py
+++ b/envs/monkey_zoo/blackbox/tests/basic_test.py
@@ -1,100 +1,8 @@
-from time import sleep
+import abc
-import logging
-from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer
-
-MAX_TIME_FOR_MONKEYS_TO_DIE = 5 * 60
-WAIT_TIME_BETWEEN_REQUESTS = 10
-TIME_FOR_MONKEY_PROCESS_TO_FINISH = 40
-DELAY_BETWEEN_ANALYSIS = 3
-LOGGER = logging.getLogger(__name__)
-
-
-class BasicTest(object):
-
- def __init__(self, name, island_client, config_parser, analyzers, timeout, post_exec_analyzers, log_handler):
- self.name = name
- self.island_client = island_client
- self.config_parser = config_parser
- self.analyzers = analyzers
- self.post_exec_analyzers = post_exec_analyzers
- self.timeout = timeout
- self.log_handler = log_handler
+class BasicTest(abc.ABC):
+ @abc.abstractmethod
def run(self):
- self.island_client.import_config(self.config_parser.config_raw)
- self.print_test_starting_info()
- try:
- self.island_client.run_monkey_local()
- self.test_until_timeout()
- finally:
- self.island_client.kill_all_monkeys()
- self.wait_until_monkeys_die()
- self.wait_for_monkey_process_to_finish()
- self.test_post_exec_analyzers()
- self.parse_logs()
- self.island_client.reset_env()
-
- def print_test_starting_info(self):
- LOGGER.info("Started {} test".format(self.name))
- LOGGER.info("Machines participating in test: " + ", ".join(self.config_parser.get_ips_of_targets()))
- print("")
-
- def test_until_timeout(self):
- timer = TestTimer(self.timeout)
- while not timer.is_timed_out():
- if self.all_analyzers_pass():
- self.log_success(timer)
- return
- sleep(DELAY_BETWEEN_ANALYSIS)
- LOGGER.debug("Waiting until all analyzers passed. Time passed: {}".format(timer.get_time_taken()))
- self.log_failure(timer)
- assert False
-
- def log_success(self, timer):
- LOGGER.info(self.get_analyzer_logs())
- LOGGER.info("{} test passed, time taken: {:.1f} seconds.".format(self.name, timer.get_time_taken()))
-
- def log_failure(self, timer):
- LOGGER.info(self.get_analyzer_logs())
- LOGGER.error("{} test failed because of timeout. Time taken: {:.1f} seconds.".format(self.name,
- timer.get_time_taken()))
-
- def all_analyzers_pass(self):
- analyzers_results = [analyzer.analyze_test_results() for analyzer in self.analyzers]
- return all(analyzers_results)
-
- def get_analyzer_logs(self):
- log = ""
- for analyzer in self.analyzers:
- log += "\n" + analyzer.log.get_contents()
- return log
-
- def wait_until_monkeys_die(self):
- time_passed = 0
- while not self.island_client.is_all_monkeys_dead() and time_passed < MAX_TIME_FOR_MONKEYS_TO_DIE:
- sleep(WAIT_TIME_BETWEEN_REQUESTS)
- time_passed += WAIT_TIME_BETWEEN_REQUESTS
- LOGGER.debug("Waiting for all monkeys to die. Time passed: {}".format(time_passed))
- if time_passed > MAX_TIME_FOR_MONKEYS_TO_DIE:
- LOGGER.error("Some monkeys didn't die after the test, failing")
- assert False
-
- def parse_logs(self):
- LOGGER.info("Parsing test logs:")
- self.log_handler.parse_test_logs()
-
- @staticmethod
- def wait_for_monkey_process_to_finish():
- """
- There is a time period when monkey is set to dead, but the process is still closing.
- If we try to launch monkey during that time window monkey will fail to start, that's
- why test needs to wait a bit even after all monkeys are dead.
- """
- LOGGER.debug("Waiting for Monkey process to close...")
- sleep(TIME_FOR_MONKEY_PROCESS_TO_FINISH)
-
- def test_post_exec_analyzers(self):
- post_exec_analyzers_results = [analyzer.analyze_test_results() for analyzer in self.post_exec_analyzers]
- assert all(post_exec_analyzers_results)
+ pass
diff --git a/envs/monkey_zoo/blackbox/tests/exploitation.py b/envs/monkey_zoo/blackbox/tests/exploitation.py
new file mode 100644
index 00000000000..e731d8f9000
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/tests/exploitation.py
@@ -0,0 +1,95 @@
+from time import sleep
+
+import logging
+
+from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer
+from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
+
+MAX_TIME_FOR_MONKEYS_TO_DIE = 5 * 60
+WAIT_TIME_BETWEEN_REQUESTS = 10
+TIME_FOR_MONKEY_PROCESS_TO_FINISH = 40
+DELAY_BETWEEN_ANALYSIS = 3
+LOGGER = logging.getLogger(__name__)
+
+
+class ExploitationTest(BasicTest):
+
+ def __init__(self, name, island_client, config_parser, analyzers, timeout, log_handler):
+ self.name = name
+ self.island_client = island_client
+ self.config_parser = config_parser
+ self.analyzers = analyzers
+ self.timeout = timeout
+ self.log_handler = log_handler
+
+ def run(self):
+ self.island_client.import_config(self.config_parser.config_raw)
+ self.print_test_starting_info()
+ try:
+ self.island_client.run_monkey_local()
+ self.test_until_timeout()
+ finally:
+ self.island_client.kill_all_monkeys()
+ self.wait_until_monkeys_die()
+ self.wait_for_monkey_process_to_finish()
+ self.parse_logs()
+ self.island_client.reset_env()
+
+ def print_test_starting_info(self):
+ LOGGER.info("Started {} test".format(self.name))
+ LOGGER.info("Machines participating in test: " + ", ".join(self.config_parser.get_ips_of_targets()))
+ print("")
+
+ def test_until_timeout(self):
+ timer = TestTimer(self.timeout)
+ while not timer.is_timed_out():
+ if self.all_analyzers_pass():
+ self.log_success(timer)
+ return
+ sleep(DELAY_BETWEEN_ANALYSIS)
+ LOGGER.debug("Waiting until all analyzers passed. Time passed: {}".format(timer.get_time_taken()))
+ self.log_failure(timer)
+ assert False
+
+ def log_success(self, timer):
+ LOGGER.info(self.get_analyzer_logs())
+ LOGGER.info("{} test passed, time taken: {:.1f} seconds.".format(self.name, timer.get_time_taken()))
+
+ def log_failure(self, timer):
+ LOGGER.info(self.get_analyzer_logs())
+ LOGGER.error("{} test failed because of timeout. Time taken: {:.1f} seconds.".format(self.name,
+ timer.get_time_taken()))
+
+ def all_analyzers_pass(self):
+ analyzers_results = [analyzer.analyze_test_results() for analyzer in self.analyzers]
+ return all(analyzers_results)
+
+ def get_analyzer_logs(self):
+ log = ""
+ for analyzer in self.analyzers:
+ log += "\n" + analyzer.log.get_contents()
+ return log
+
+ def wait_until_monkeys_die(self):
+ time_passed = 0
+ while not self.island_client.is_all_monkeys_dead() and time_passed < MAX_TIME_FOR_MONKEYS_TO_DIE:
+ sleep(WAIT_TIME_BETWEEN_REQUESTS)
+ time_passed += WAIT_TIME_BETWEEN_REQUESTS
+ LOGGER.debug("Waiting for all monkeys to die. Time passed: {}".format(time_passed))
+ if time_passed > MAX_TIME_FOR_MONKEYS_TO_DIE:
+ LOGGER.error("Some monkeys didn't die after the test, failing")
+ assert False
+
+ def parse_logs(self):
+ LOGGER.info("Parsing test logs:")
+ self.log_handler.parse_test_logs()
+
+ @staticmethod
+ def wait_for_monkey_process_to_finish():
+ """
+ There is a time period when monkey is set to dead, but the process is still closing.
+ If we try to launch monkey during that time window monkey will fail to start, that's
+ why test needs to wait a bit even after all monkeys are dead.
+ """
+ LOGGER.debug("Waiting for Monkey process to close...")
+ sleep(TIME_FOR_MONKEY_PROCESS_TO_FINISH)
diff --git a/envs/monkey_zoo/blackbox/tests/performance/__init__.py b/envs/monkey_zoo/blackbox/tests/performance/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/envs/monkey_zoo/blackbox/tests/performance/endpoint_performance_test.py b/envs/monkey_zoo/blackbox/tests/performance/endpoint_performance_test.py
new file mode 100644
index 00000000000..76a389efdd1
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/tests/performance/endpoint_performance_test.py
@@ -0,0 +1,42 @@
+import logging
+from datetime import timedelta
+
+from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
+from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient
+from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
+from envs.monkey_zoo.blackbox.analyzers.performance_analyzer import PerformanceAnalyzer
+
+
+LOGGER = logging.getLogger(__name__)
+
+
+class EndpointPerformanceTest(BasicTest):
+
+ def __init__(self, name, test_config: PerformanceTestConfig, island_client: MonkeyIslandClient):
+ self.name = name
+ self.test_config = test_config
+ self.island_client = island_client
+
+ def run(self) -> bool:
+ if not self.island_client.is_all_monkeys_dead():
+ raise RuntimeError("Can't test report times since not all Monkeys have died.")
+
+ # Collect timings for all pages
+ self.island_client.clear_caches()
+ endpoint_timings = {}
+ for endpoint in self.test_config.endpoints_to_test:
+ endpoint_timings[endpoint] = self.get_elapsed_for_get_request(endpoint)
+
+ analyzer = PerformanceAnalyzer(self.test_config, endpoint_timings)
+
+ return analyzer.analyze_test_results()
+
+ def get_elapsed_for_get_request(self, url):
+ response = self.island_client.requests.get(url)
+ if response.ok:
+ LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120].strip()}")
+ return response.elapsed
+ else:
+ LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}")
+ # instead of raising for status, mark failed responses as maxtime
+ return timedelta.max
diff --git a/envs/monkey_zoo/blackbox/tests/performance/map_generation.py b/envs/monkey_zoo/blackbox/tests/performance/map_generation.py
new file mode 100644
index 00000000000..c597907f409
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/tests/performance/map_generation.py
@@ -0,0 +1,35 @@
+from datetime import timedelta
+
+from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
+from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
+from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest
+from envs.monkey_zoo.blackbox.tests.performance.performance_test_workflow import PerformanceTestWorkflow
+
+MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2)
+MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5)
+
+MAP_RESOURCES = [
+ "api/netmap",
+]
+
+
+class MapGenerationTest(PerformanceTest):
+
+ TEST_NAME = "Map generation performance test"
+
+ def __init__(self, island_client, config_parser, analyzers,
+ timeout, log_handler, break_on_timeout):
+ self.island_client = island_client
+ self.config_parser = config_parser
+ exploitation_test = ExploitationTest(MapGenerationTest.TEST_NAME, island_client,
+ config_parser, analyzers, timeout, log_handler)
+ performance_config = PerformanceTestConfig(max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
+ max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
+ endpoints_to_test=MAP_RESOURCES,
+ break_on_timeout=break_on_timeout)
+ self.performance_test_workflow = PerformanceTestWorkflow(MapGenerationTest.TEST_NAME,
+ exploitation_test,
+ performance_config)
+
+ def run(self):
+ self.performance_test_workflow.run()
diff --git a/envs/monkey_zoo/blackbox/tests/performance/performance_test.py b/envs/monkey_zoo/blackbox/tests/performance/performance_test.py
new file mode 100644
index 00000000000..b26c20f9396
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/tests/performance/performance_test.py
@@ -0,0 +1,16 @@
+from abc import ABCMeta, abstractmethod
+
+from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
+
+
+class PerformanceTest(BasicTest, metaclass=ABCMeta):
+
+ @abstractmethod
+ def __init__(self, island_client, config_parser, analyzers,
+ timeout, log_handler, break_on_timeout):
+ pass
+
+ @property
+ @abstractmethod
+ def TEST_NAME(self):
+ pass
diff --git a/envs/monkey_zoo/blackbox/tests/performance/performance_test_config.py b/envs/monkey_zoo/blackbox/tests/performance/performance_test_config.py
new file mode 100644
index 00000000000..8ed2b5a626a
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/tests/performance/performance_test_config.py
@@ -0,0 +1,12 @@
+from datetime import timedelta
+from typing import List
+
+
+class PerformanceTestConfig:
+
+ def __init__(self, max_allowed_single_page_time: timedelta, max_allowed_total_time: timedelta,
+ endpoints_to_test: List[str], break_on_timeout=False):
+ self.max_allowed_single_page_time = max_allowed_single_page_time
+ self.max_allowed_total_time = max_allowed_total_time
+ self.endpoints_to_test = endpoints_to_test
+ self.break_on_timeout = break_on_timeout
diff --git a/envs/monkey_zoo/blackbox/tests/performance/performance_test_workflow.py b/envs/monkey_zoo/blackbox/tests/performance/performance_test_workflow.py
new file mode 100644
index 00000000000..3157140a981
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/tests/performance/performance_test_workflow.py
@@ -0,0 +1,31 @@
+from envs.monkey_zoo.blackbox.tests.basic_test import BasicTest
+from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
+from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
+from envs.monkey_zoo.blackbox.tests.performance.endpoint_performance_test import EndpointPerformanceTest
+
+
+class PerformanceTestWorkflow(BasicTest):
+
+ def __init__(self, name, exploitation_test: ExploitationTest, performance_config: PerformanceTestConfig):
+ self.name = name
+ self.exploitation_test = exploitation_test
+ self.island_client = exploitation_test.island_client
+ self.config_parser = exploitation_test.config_parser
+ self.performance_config = performance_config
+
+ def run(self):
+ self.island_client.import_config(self.config_parser.config_raw)
+ self.exploitation_test.print_test_starting_info()
+ try:
+ self.island_client.run_monkey_local()
+ self.exploitation_test.test_until_timeout()
+ finally:
+ self.island_client.kill_all_monkeys()
+ self.exploitation_test.wait_until_monkeys_die()
+ self.exploitation_test.wait_for_monkey_process_to_finish()
+ performance_test = EndpointPerformanceTest(self.name, self.performance_config, self.island_client)
+ try:
+ assert performance_test.run()
+ finally:
+ self.exploitation_test.parse_logs()
+ self.island_client.reset_env()
diff --git a/envs/monkey_zoo/blackbox/tests/performance/report_generation.py b/envs/monkey_zoo/blackbox/tests/performance/report_generation.py
new file mode 100644
index 00000000000..52fe762886c
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/tests/performance/report_generation.py
@@ -0,0 +1,38 @@
+from datetime import timedelta
+
+from envs.monkey_zoo.blackbox.tests.exploitation import ExploitationTest
+from envs.monkey_zoo.blackbox.tests.performance.performance_test_config import PerformanceTestConfig
+from envs.monkey_zoo.blackbox.tests.performance.performance_test_workflow import PerformanceTestWorkflow
+from envs.monkey_zoo.blackbox.tests.performance.performance_test import PerformanceTest
+
+MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2)
+MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5)
+
+REPORT_RESOURCES = [
+ "api/report/security",
+ "api/attack/report",
+ "api/report/zero_trust/findings",
+ "api/report/zero_trust/principles",
+ "api/report/zero_trust/pillars"
+]
+
+
+class ReportGenerationTest(PerformanceTest):
+ TEST_NAME = "Report generation performance test"
+
+ def __init__(self, island_client, config_parser, analyzers,
+ timeout, log_handler, break_on_timeout):
+ self.island_client = island_client
+ self.config_parser = config_parser
+ exploitation_test = ExploitationTest(ReportGenerationTest.TEST_NAME, island_client,
+ config_parser, analyzers, timeout, log_handler)
+ performance_config = PerformanceTestConfig(max_allowed_single_page_time=MAX_ALLOWED_SINGLE_PAGE_TIME,
+ max_allowed_total_time=MAX_ALLOWED_TOTAL_TIME,
+ endpoints_to_test=REPORT_RESOURCES,
+ break_on_timeout=break_on_timeout)
+ self.performance_test_workflow = PerformanceTestWorkflow(ReportGenerationTest.TEST_NAME,
+ exploitation_test,
+ performance_config)
+
+ def run(self):
+ self.performance_test_workflow.run()
diff --git a/monkey/common/version.py b/monkey/common/version.py
index 9d60e636ca1..fd706d90924 100644
--- a/monkey/common/version.py
+++ b/monkey/common/version.py
@@ -4,7 +4,7 @@
MAJOR = "1"
MINOR = "8"
-PATCH = "0"
+PATCH = "1"
build_file_path = Path(__file__).parent.joinpath("BUILD")
with open(build_file_path, "r") as build_file:
BUILD = build_file.read()
diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py
index 82954b99bd1..1305e394632 100644
--- a/monkey/infection_monkey/exploit/vsftpd.py
+++ b/monkey/infection_monkey/exploit/vsftpd.py
@@ -129,7 +129,7 @@ def _exploit_host(self):
change_permission = str.encode(str(change_permission) + '\n')
LOG.info("change_permission command is %s", change_permission)
backdoor_socket.send(change_permission)
- T1222Telem(ScanStatus.USED, change_permission, self.host).send()
+ T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send()
# Run monkey on the machine
parameters = build_monkey_commandline(self.host, get_monkey_depth() - 1)
@@ -143,7 +143,7 @@ def _exploit_host(self):
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_executed_cmd(run_monkey)
+ self.add_executed_cmd(run_monkey.decode())
return True
else:
return False
diff --git a/monkey/infection_monkey/requirements.txt b/monkey/infection_monkey/requirements.txt
index 7dd61cd19f2..858e5652b18 100644
--- a/monkey/infection_monkey/requirements.txt
+++ b/monkey/infection_monkey/requirements.txt
@@ -9,7 +9,11 @@ git+https://github.com/guardicore/pyinstaller
ecdsa
netifaces
ipaddress
-wmi
+# Locking WMI since version 1.5 introduced breaking change on Linux agent compilation.
+# See breaking change here: https://github.com/tjguk/wmi/commit/dcf8e3eca79bb8c0101ffb83e25c066b0ba9e16d
+# Causes pip to error with:
+# Could not find a version that satisfies the requirement pywin32 (from wmi->-r /src/infection_monkey/requirements.txt (line 12)) (from versions: none)
+wmi==1.4.9
pywin32 ; sys_platform == 'win32'
pymssql<3.0
pyftpdlib
diff --git a/monkey/infection_monkey/telemetry/base_telem.py b/monkey/infection_monkey/telemetry/base_telem.py
index 2d59b9cbf0e..7617ab4e394 100644
--- a/monkey/infection_monkey/telemetry/base_telem.py
+++ b/monkey/infection_monkey/telemetry/base_telem.py
@@ -17,12 +17,16 @@ class BaseTelem(object, metaclass=abc.ABCMeta):
def __init__(self):
pass
- def send(self):
+ def send(self, log_data=True):
"""
Sends telemetry to island
"""
data = self.get_data()
- logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, json.dumps(data)))
+ if log_data:
+ data_to_log = json.dumps(data)
+ else:
+ data_to_log = 'redacted'
+ logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, data_to_log))
ControlClient.send_telemetry(self.telem_category, data)
@property
diff --git a/monkey/infection_monkey/telemetry/system_info_telem.py b/monkey/infection_monkey/telemetry/system_info_telem.py
index a4b1c0bd0e0..69ee7beda9e 100644
--- a/monkey/infection_monkey/telemetry/system_info_telem.py
+++ b/monkey/infection_monkey/telemetry/system_info_telem.py
@@ -17,3 +17,6 @@ def __init__(self, system_info):
def get_data(self):
return self.system_info
+
+ def send(self, log_data=False):
+ super(SystemInfoTelem, self).send(log_data)
diff --git a/monkey/monkey_island/cc/environment/__init__.py b/monkey/monkey_island/cc/environment/__init__.py
index ec7c7a0f441..195778e16d8 100644
--- a/monkey/monkey_island/cc/environment/__init__.py
+++ b/monkey/monkey_island/cc/environment/__init__.py
@@ -1,7 +1,7 @@
from abc import ABCMeta, abstractmethod
from datetime import timedelta
import os
-from Crypto.Hash import SHA3_512
+import hashlib
__author__ = 'itay.mizeretz'
@@ -45,10 +45,11 @@ def is_debug(self):
def get_auth_expiration_time(self):
return self._AUTH_EXPIRATION_TIME
- def hash_secret(self, secret):
- h = SHA3_512.new()
- h.update(secret)
- return h.hexdigest()
+ @staticmethod
+ def hash_secret(secret):
+ hash_obj = hashlib.sha3_512()
+ hash_obj.update(secret.encode('utf-8'))
+ return hash_obj.hexdigest()
def get_deployment(self):
return self._get_from_config('deployment', 'unknown')
diff --git a/monkey/monkey_island/cc/environment/test_aws.py b/monkey/monkey_island/cc/environment/test_aws.py
new file mode 100644
index 00000000000..222e975304d
--- /dev/null
+++ b/monkey/monkey_island/cc/environment/test_aws.py
@@ -0,0 +1,26 @@
+from monkey_island.cc.auth import User
+from monkey_island.cc.testing.IslandTestCase import IslandTestCase
+from monkey_island.cc.environment.aws import AwsEnvironment
+
+import hashlib
+
+
+class TestAwsEnvironment(IslandTestCase):
+ def test_get_auth_users(self):
+ env = AwsEnvironment()
+ # This is "injecting" the instance id to the env. This is the UTs aren't always executed on the same AWS machine
+ # (might not be an AWS machine at all). Perhaps it would have been more elegant to create a Mock, but not worth it for
+ # this small test.
+ env._instance_id = "i-666"
+ hash_obj = hashlib.sha3_512()
+ hash_obj.update(b"i-666")
+ auth_users = env.get_auth_users()
+ assert isinstance(auth_users, list)
+ assert len(auth_users) == 1
+ auth_user = auth_users[0]
+ assert isinstance(auth_user, User)
+ assert auth_user.id == 1
+ assert auth_user.username == "monkey"
+ assert auth_user.secret == hash_obj.hexdigest()
+
+
diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py
index 09f079c19eb..50e3bdd7c64 100644
--- a/monkey/monkey_island/cc/main.py
+++ b/monkey/monkey_island/cc/main.py
@@ -31,7 +31,7 @@
from monkey_island.cc.setup import setup
-def main(should_setup_only):
+def main(should_setup_only=False):
logger.info("Starting bootloader server")
mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url())
bootloader_server_thread = Thread(target=BootloaderHttpServer(mongo_url).serve_forever, daemon=True)
diff --git a/monkey/monkey_island/cc/services/attack/attack_data b/monkey/monkey_island/cc/services/attack/attack_data
index c139e37bdc5..fb8942b1a10 160000
--- a/monkey/monkey_island/cc/services/attack/attack_data
+++ b/monkey/monkey_island/cc/services/attack/attack_data
@@ -1 +1 @@
-Subproject commit c139e37bdc51acbc7d0488a5be48553caffdbbd7
+Subproject commit fb8942b1a10f4e734ed75542f2ccae7cbd72c46d
diff --git a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py
index ad4419be5df..6390c600ba6 100644
--- a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py
+++ b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py
@@ -1,6 +1,6 @@
from typing import List, Dict
-from stix2 import FileSystemSource, Filter, CourseOfAction, AttackPattern, v20
+from stix2 import FileSystemSource, Filter, CourseOfAction, AttackPattern
class MitreApiInterface:
@@ -32,14 +32,14 @@ def get_technique_and_mitigation_relationships() -> List[CourseOfAction]:
return all_techniques
@staticmethod
- def get_stix2_external_reference_id(stix2_data: v20._DomainObject) -> str:
+ def get_stix2_external_reference_id(stix2_data) -> str:
for reference in stix2_data['external_references']:
if reference['source_name'] == "mitre-attack" and 'external_id' in reference:
return reference['external_id']
return ''
@staticmethod
- def get_stix2_external_reference_url(stix2_data: v20._DomainObject) -> str:
+ def get_stix2_external_reference_url(stix2_data) -> str:
for reference in stix2_data['external_references']:
if 'url' in reference:
return reference['url']
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 80dbe75183a..bd4e07c2451 100644
--- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py
+++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py
@@ -5,7 +5,7 @@
from common.utils.attack_utils import ScanStatus
from monkey_island.cc.services.attack.attack_config import AttackConfig
from common.utils.code_utils import abstractstatic
-from cc.models.attack.attack_mitigations import AttackMitigations
+from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations
logger = logging.getLogger(__name__)
diff --git a/monkey/monkey_island/deb-package/DEBIAN_MONGO/control b/monkey/monkey_island/deb-package/DEBIAN_MONGO/control
index a4737100588..a7bc2373eb5 100644
--- a/monkey/monkey_island/deb-package/DEBIAN_MONGO/control
+++ b/monkey/monkey_island/deb-package/DEBIAN_MONGO/control
@@ -5,4 +5,4 @@ Homepage: https://www.infectionmonkey.com
Priority: optional
Version: 1.0
Description: Guardicore Infection Monkey Island installation package
-Depends: openssl, python3-pip, python3-dev
+Depends: openssl, python3.7-dev, python3.7-venv, python3-venv, build-essential
diff --git a/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst b/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst
index f79a71913b9..f12b31b73e2 100644
--- a/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst
+++ b/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst
@@ -1,20 +1,42 @@
#!/bin/bash
+# See the "Depends" field of the control file for what packages this scripts depends on.
+# Here are the explanations for the current deps:
+# Dependency - Why is it required
+## openssl - Server certificate generation
+## python3.7-dev - Server runtime
+## python3.7-venv - For creating virtual env to install all the server pip deps (don't want to pollute system python)
+## python3-venv - python3.7-venv doesn't work without it since you need ensure-pip
+## build-essential - for compiling python dependencies that don't come in a pre-compiled wheel, like `netifaces`
+
+echo "Installing Monkey Island (Infection Monkey server)..."
+
MONKEY_FOLDER=/var/monkey
INSTALLATION_FOLDER=/var/monkey/monkey_island/installation
PYTHON_FOLDER=/var/monkey/monkey_island/bin/python
+PYTHON_VERSION=python3.7
# Prepare python virtualenv
-pip3 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER
-python3 -m virtualenv -p python3 ${PYTHON_FOLDER}
+# This is using the apt package `python3.7-venv` which is listed in the `control` file as a dependency.
+# See https://packages.debian.org/stable/python/python3.7-venv
+echo "Using $(command -v $PYTHON_VERSION) as the base for virtualenv creation"
+$PYTHON_VERSION -m venv ${PYTHON_FOLDER}
+# shellcheck disable=SC1090
+source ${PYTHON_FOLDER}/bin/activate
+
+echo "Installing Python dependencies using $(command -v python) and $(command -v pip)..."
+# First, make sure that pip is updated
+python -m pip install --upgrade pip
+# Then install the dependecies from the pre-downloaded whl and tar.gz file
+python -m pip install -r $MONKEY_FOLDER/monkey_island/requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER
-# install pip requirements
-${PYTHON_FOLDER}/bin/python -m pip install -r $MONKEY_FOLDER/monkey_island/requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER
+deactivate
# remove installation folder and unnecessary files
rm -rf ${INSTALLATION_FOLDER}
rm -f ${MONKEY_FOLDER}/monkey_island/requirements.txt
+echo "Installing mongodb..."
${MONKEY_FOLDER}/monkey_island/install_mongo.sh ${MONKEY_FOLDER}/monkey_island/bin/mongodb
if [ -d "/etc/systemd/network" ]; then
@@ -25,11 +47,17 @@ if [ -d "/etc/systemd/network" ]; then
systemctl enable monkey-island
fi
-${MONKEY_FOLDER}/monkey_island/create_certificate.sh ${MONKEY_FOLDER}/monkey_island/
+echo "Creating server certificate..."
+${MONKEY_FOLDER}/monkey_island/create_certificate.sh ${MONKEY_FOLDER}/monkey_island/cc
+echo "Starting services..."
service monkey-island start
service monkey-mongo start
-echo Monkey Island installation ended
+echo ""
+echo "Monkey Island installation ended."
+echo "The server should be accessible soon via https://:5000/"
+echo "To check the Island's status, run 'sudo service monkey-island status'"
+echo ""
-exit 0
\ No newline at end of file
+exit 0
diff --git a/monkey/monkey_island/linux/create_certificate.sh b/monkey/monkey_island/linux/create_certificate.sh
index 7e306a82280..985f607bc94 100644
--- a/monkey/monkey_island/linux/create_certificate.sh
+++ b/monkey/monkey_island/linux/create_certificate.sh
@@ -2,8 +2,29 @@
server_root=${1:-"./cc"}
+echo "Creating server cetificate. Server root: $server_root"
+# We override the RANDFILE determined by default openssl.cnf, if it doesn't exist.
+# This is a known issue with the current version of openssl on Ubuntu 18.04 - once they release
+# a new version, we can delete this command. See
+# https://github.com/openssl/openssl/commit/0f58220973a02248ca5c69db59e615378467b9c8#diff-8ce6aaad88b10ed2b3b4592fd5c8e03a
+# for more details.
+DEFAULT_RND_FILE_PATH=~/.rnd
+CREATED_RND_FILE=false
+if [ ! -f /tmp/foo.txt ]; then # If the file already exists, assume that the contents are fine, and don't change them.
+ echo "Creating rand seed file in $DEFAULT_RND_FILE_PATH"
+ dd bs=1024 count=2 "$DEFAULT_RND_FILE_PATH"
+ chmod 666 "$DEFAULT_RND_FILE_PATH"
+ CREATED_RND_FILE=true
+fi
+echo "Generating key in $server_root/server.key..."
openssl genrsa -out "$server_root"/server.key 2048
+echo "Generating csr in $server_root/server.csr..."
openssl req -new -key "$server_root"/server.key -out "$server_root"/server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com"
-openssl x509 -req -days 366 -in "$server_root"/server.csr -signkey "$server_root"/server.key -out $server_root/server.crt
+echo "Generating certificate in $server_root/server.crt..."
+openssl x509 -req -days 366 -in "$server_root"/server.csr -signkey "$server_root"/server.key -out "$server_root"/server.crt
+# Shove some new random data into the file to override the original seed we put in.
+if [ "$CREATED_RND_FILE" = true ] ; then
+ dd bs=1024 count=2 "$DEFAULT_RND_FILE_PATH"
+fi
diff --git a/monkey/monkey_island/monkey_island.spec b/monkey/monkey_island/monkey_island.spec
index ef6d9c2d33e..59f95e34f91 100644
--- a/monkey/monkey_island/monkey_island.spec
+++ b/monkey/monkey_island/monkey_island.spec
@@ -1,7 +1,7 @@
# -*- mode: python -*-
import os
import platform
-
+import sys
__author__ = 'itay.mizeretz'
@@ -9,15 +9,20 @@ block_cipher = None
def main():
+ # These data files and folders will be included in the bundle.
+ # The format of the tuples is (src, dest_dir). See https://pythonhosted.org/PyInstaller/spec-files.html#adding-data-files
+ added_datas = [
+ ("../common/BUILD", "/common"),
+ ("../monkey_island/cc/services/attack/attack_data", "/monkey_island/cc/services/attack/attack_data")
+ ]
+
a = Analysis(['cc/main.py'],
pathex=['..'],
hiddenimports=get_hidden_imports(),
- hookspath=None,
+ hookspath=[os.path.join(".", "pyinstaller_hooks")],
runtime_hooks=None,
binaries=None,
- datas=[
- ("../common/BUILD", "/common")
- ],
+ datas=added_datas,
excludes=None,
win_no_prefer_redirects=None,
win_private_assemblies=None,
@@ -36,8 +41,7 @@ def main():
name=get_monkey_filename(),
debug=False,
strip=get_exe_strip(),
- upx=True,
- upx_exclude=['vcruntime140.dll'],
+ upx=False,
console=True,
icon=get_exe_icon())
@@ -74,7 +78,7 @@ def get_linux_only_binaries():
def get_hidden_imports():
- return ['_cffi_backend', 'queue'] if is_windows() else ['_cffi_backend']
+ return ['_cffi_backend', 'queue', 'pkg_resources.py2_warn'] if is_windows() else ['_cffi_backend']
def get_msvcr():
diff --git a/monkey/monkey_island/pyinstaller_hooks/__init__.py b/monkey/monkey_island/pyinstaller_hooks/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py b/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py
new file mode 100644
index 00000000000..22a9c977481
--- /dev/null
+++ b/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py
@@ -0,0 +1,7 @@
+# Workaround for packaging Monkey Island using PyInstaller. See https://github.com/oasis-open/cti-python-stix2/issues/218
+
+import os
+from PyInstaller.utils.hooks import get_module_file_attribute
+
+stix2_dir = os.path.dirname(get_module_file_attribute('stix2'))
+datas = [(stix2_dir, 'stix2')]
diff --git a/monkey/monkey_island/requirements.txt b/monkey/monkey_island/requirements.txt
index cad53d1c8a5..b5baed7f4d5 100644
--- a/monkey/monkey_island/requirements.txt
+++ b/monkey/monkey_island/requirements.txt
@@ -1,5 +1,4 @@
pytest
-bson
python-dateutil
tornado
werkzeug