forked from p15670423/monkey
CR comments fixed
This commit is contained in:
parent
72e30bb631
commit
73d434119d
|
@ -1,4 +1,5 @@
|
|||
# Automatic blackbox tests
|
||||
### Prerequisites
|
||||
1. Download google sdk: https://cloud.google.com/sdk/docs/
|
||||
2. Download service account key GCP console -> IAM -> service accounts(you can use the same key used to authenticate terraform scripts)
|
||||
2. Download service account key for MonkeyZoo project (if you deployed MonkeyZoo via terraform scripts then you already have it).
|
||||
GCP console -> IAM -> service accounts(you can use the same key used to authenticate terraform scripts)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from time import sleep
|
||||
import json
|
||||
|
||||
import logging
|
||||
from bson import json_util
|
||||
|
||||
from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests
|
||||
|
@ -30,9 +31,9 @@ class MonkeyIslandClient(object):
|
|||
def run_monkey_local(self):
|
||||
response = self.requests.post_json("api/local-monkey", dict_data={"action": "run"})
|
||||
if MonkeyIslandClient.monkey_ran_successfully(response):
|
||||
print("Running the monkey.")
|
||||
logging.info("Running the monkey.")
|
||||
else:
|
||||
print("Failed to run the monkey.")
|
||||
logging.error("Failed to run the monkey.")
|
||||
assert False
|
||||
|
||||
@staticmethod
|
||||
|
@ -42,36 +43,35 @@ class MonkeyIslandClient(object):
|
|||
@avoid_race_condition
|
||||
def kill_all_monkeys(self):
|
||||
if self.requests.get("api", {"action": "killall"}).ok:
|
||||
print("Killing all monkeys after the test.")
|
||||
logging.info("Killing all monkeys after the test.")
|
||||
else:
|
||||
print("Failed to kill all monkeys.")
|
||||
logging.error("Failed to kill all monkeys.")
|
||||
assert False
|
||||
|
||||
@avoid_race_condition
|
||||
def reset_env(self):
|
||||
if self.requests.get("api", {"action": "reset"}).ok:
|
||||
print("Resetting environment after the test.")
|
||||
logging.info("Resetting environment after the test.")
|
||||
else:
|
||||
print("Failed to reset the environment.")
|
||||
logging.error("Failed to reset the environment.")
|
||||
assert False
|
||||
|
||||
def find_monkeys_in_db(self, query):
|
||||
if query is None:
|
||||
raise TypeError
|
||||
response = self.requests.get(MONKEY_TEST_ENDPOINT,
|
||||
MonkeyIslandClient.form_find_query_for_request(query))
|
||||
try:
|
||||
return MonkeyIslandClient.get_test_query_results(response)
|
||||
except Exception:
|
||||
print("Ran into trouble parsing response for monkey query")
|
||||
raise
|
||||
return MonkeyIslandClient.get_test_query_results(response)
|
||||
|
||||
def get_all_monkeys_from_db(self):
|
||||
response = self.requests.get(MONKEY_TEST_ENDPOINT,
|
||||
MonkeyIslandClient.form_find_query_for_request(None))
|
||||
return MonkeyIslandClient.get_test_query_results(response)
|
||||
|
||||
def find_log_in_db(self, query):
|
||||
response = self.requests.get(LOG_TEST_ENDPOINT,
|
||||
MonkeyIslandClient.form_find_query_for_request(query))
|
||||
try:
|
||||
return MonkeyIslandClient.get_test_query_results(response)
|
||||
except Exception:
|
||||
print("Ran into trouble parsing response for log query")
|
||||
raise
|
||||
return MonkeyIslandClient.get_test_query_results(response)
|
||||
|
||||
@staticmethod
|
||||
def form_find_query_for_request(query):
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import requests
|
||||
|
||||
# SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()'
|
||||
import logging
|
||||
|
||||
NO_AUTH_CREDS = '55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d2062' \
|
||||
'8d2c8d0b1538d2208c1444ac66535b764a3d902b35e751df3faec1e477ed3557'
|
||||
|
||||
|
@ -14,7 +16,7 @@ class MonkeyIslandRequests(object):
|
|||
try:
|
||||
return self.get_jwt_from_server()
|
||||
except requests.ConnectionError:
|
||||
print("Unable to connect to island, aborting!")
|
||||
logging.error("Unable to connect to island, aborting!")
|
||||
assert False
|
||||
|
||||
def get_jwt_from_server(self):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
|
||||
import logging
|
||||
from bson import ObjectId
|
||||
|
||||
|
||||
|
@ -11,7 +12,7 @@ class MonkeyLog(object):
|
|||
def download_log(self, island_client):
|
||||
log = island_client.find_log_in_db({'monkey_id': ObjectId(self.monkey['id'])})
|
||||
if not log:
|
||||
print("Log for monkey {} not found".format(self.monkey['ip_addresses'][0]))
|
||||
logging.error("Log for monkey {} not found".format(self.monkey['ip_addresses'][0]))
|
||||
return False
|
||||
else:
|
||||
self.write_log_to_file(log)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import re
|
||||
|
||||
|
||||
class LogParser(object):
|
||||
class MonkeyLogParser(object):
|
||||
|
||||
def __init__(self, log_path):
|
||||
self.log_path = log_path
|
||||
|
@ -13,7 +13,7 @@ class LogParser(object):
|
|||
|
||||
def print_errors(self):
|
||||
print("Errors:")
|
||||
for error_line in LogParser.get_errors(self.log_contents):
|
||||
for error_line in MonkeyLogParser.get_errors(self.log_contents):
|
||||
print(error_line)
|
||||
|
||||
@staticmethod
|
||||
|
@ -23,7 +23,7 @@ class LogParser(object):
|
|||
|
||||
def print_warnings(self):
|
||||
print("Warnings:")
|
||||
for warning_line in LogParser.get_warnings(self.log_contents):
|
||||
for warning_line in MonkeyLogParser.get_warnings(self.log_contents):
|
||||
print(warning_line)
|
||||
|
||||
@staticmethod
|
|
@ -1,7 +1,9 @@
|
|||
import logging
|
||||
|
||||
from envs.monkey_zoo.blackbox.log_handlers.monkey_log import MonkeyLog
|
||||
|
||||
|
||||
class LogsDownloader(object):
|
||||
class MonkeyLogsDownloader(object):
|
||||
|
||||
def __init__(self, island_client, log_dir_path):
|
||||
self.island_client = island_client
|
||||
|
@ -9,8 +11,8 @@ class LogsDownloader(object):
|
|||
self.monkey_log_paths = []
|
||||
|
||||
def download_monkey_logs(self):
|
||||
print("Downloading each monkey log.")
|
||||
all_monkeys = self.island_client.find_monkeys_in_db(None)
|
||||
logging.info("Downloading each monkey log.")
|
||||
all_monkeys = self.island_client.get_all_monkeys_from_db()
|
||||
for monkey in all_monkeys:
|
||||
downloaded_log_path = self._download_monkey_log(monkey)
|
||||
if downloaded_log_path:
|
|
@ -1,28 +1,31 @@
|
|||
import os
|
||||
import shutil
|
||||
|
||||
from envs.monkey_zoo.blackbox.log_handlers.log_parser import LogParser
|
||||
from envs.monkey_zoo.blackbox.log_handlers.logs_downloader import LogsDownloader
|
||||
import logging
|
||||
|
||||
from envs.monkey_zoo.blackbox.log_handlers.monkey_log_parser import MonkeyLogParser
|
||||
from envs.monkey_zoo.blackbox.log_handlers.monkey_logs_downloader import MonkeyLogsDownloader
|
||||
|
||||
LOG_DIR_NAME = 'logs'
|
||||
|
||||
|
||||
class TestLogsHandler(object):
|
||||
def __init__(self, test_name, island_client):
|
||||
def __init__(self, test_name, island_client, log_dir_path):
|
||||
self.test_name = test_name
|
||||
self.island_client = island_client
|
||||
self.log_dir_path = os.path.join(TestLogsHandler.get_log_dir_path(), self.test_name)
|
||||
self.log_dir_path = os.path.join(log_dir_path, self.test_name)
|
||||
|
||||
def parse_test_logs(self):
|
||||
log_paths = self.download_logs()
|
||||
if not log_paths:
|
||||
print("No logs were downloaded, maybe no monkeys were ran?")
|
||||
logging.error("No logs were downloaded. Maybe no monkeys were ran "
|
||||
"or early exception prevented log download?")
|
||||
return
|
||||
TestLogsHandler.parse_logs(log_paths)
|
||||
|
||||
def download_logs(self):
|
||||
self.try_create_log_dir_for_test()
|
||||
downloader = LogsDownloader(self.island_client, self.log_dir_path)
|
||||
downloader = MonkeyLogsDownloader(self.island_client, self.log_dir_path)
|
||||
downloader.download_monkey_logs()
|
||||
return downloader.monkey_log_paths
|
||||
|
||||
|
@ -30,21 +33,17 @@ class TestLogsHandler(object):
|
|||
try:
|
||||
os.mkdir(self.log_dir_path)
|
||||
except Exception as e:
|
||||
print("Can't create a dir for test logs: {}".format(e))
|
||||
logging.error("Can't create a dir for test logs: {}".format(e))
|
||||
|
||||
@staticmethod
|
||||
def get_log_dir_path():
|
||||
return os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), LOG_DIR_NAME)
|
||||
|
||||
@staticmethod
|
||||
def delete_log_folder_contents():
|
||||
shutil.rmtree(TestLogsHandler.get_log_dir_path(), ignore_errors=True)
|
||||
os.mkdir(TestLogsHandler.get_log_dir_path())
|
||||
def delete_log_folder_contents(log_dir_path):
|
||||
shutil.rmtree(log_dir_path, ignore_errors=True)
|
||||
os.mkdir(log_dir_path)
|
||||
|
||||
@staticmethod
|
||||
def parse_logs(log_paths):
|
||||
for log_path in log_paths:
|
||||
print("Info from log at {}".format(log_path))
|
||||
log_parser = LogParser(log_path)
|
||||
logging.info("Info from log at {}".format(log_path))
|
||||
log_parser = MonkeyLogParser(log_path)
|
||||
log_parser.print_errors()
|
||||
log_parser.print_warnings()
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pytest
|
||||
unittest
|
||||
unittest
|
||||
pytest-random-order
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import os
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
from time import sleep
|
||||
|
||||
|
@ -13,6 +16,7 @@ MACHINE_BOOTUP_WAIT_SECONDS = 30
|
|||
GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'haddop-2-v3', 'hadoop-3', 'mssql-16',
|
||||
'mimikatz-14', 'mimikatz-15', 'final-test-struts2-23', 'final-test-struts2-24',
|
||||
'tunneling-9', 'tunneling-10', 'tunneling-11', 'weblogic-18', 'weblogic-19', 'shellshock-8']
|
||||
LOG_DIR_PATH = "./logs"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope='session')
|
||||
|
@ -29,8 +33,8 @@ def GCPHandler(request):
|
|||
|
||||
@pytest.fixture(autouse=True, scope='session')
|
||||
def delete_logs():
|
||||
print("Deleting monkey logs before new tests.")
|
||||
TestLogsHandler.delete_log_folder_contents()
|
||||
logging.info("Deleting monkey logs before new tests.")
|
||||
TestLogsHandler.delete_log_folder_contents(TestMonkeyBlackbox.get_log_dir_path())
|
||||
|
||||
|
||||
def wait_machine_bootup():
|
||||
|
@ -52,11 +56,17 @@ class TestMonkeyBlackbox(object):
|
|||
def run_basic_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(test_name,
|
||||
island_client,
|
||||
config_parser,
|
||||
[analyzer],
|
||||
timeout_in_seconds).run()
|
||||
timeout_in_seconds,
|
||||
log_handler).run()
|
||||
|
||||
@staticmethod
|
||||
def get_log_dir_path():
|
||||
return os.path.abspath(LOG_DIR_PATH)
|
||||
|
||||
def test_server_online(self, island_client):
|
||||
assert island_client.get_api_status() is not None
|
||||
|
@ -75,7 +85,7 @@ class TestMonkeyBlackbox(object):
|
|||
|
||||
def test_smb_pth(self, island_client):
|
||||
TestMonkeyBlackbox.run_basic_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")
|
||||
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
from time import sleep
|
||||
|
||||
import logging
|
||||
|
||||
from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer
|
||||
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler
|
||||
|
||||
MAX_TIME_FOR_MONKEYS_TO_DIE = 5*60
|
||||
WAIT_TIME_BETWEEN_REQUESTS = 10
|
||||
TIME_FOR_MONKEY_PROCESS_TO_FINISH = 40
|
||||
DELAY_BETWEEN_ANALYSIS = 3
|
||||
|
||||
|
||||
class BasicTest(object):
|
||||
|
||||
def __init__(self, name, island_client, config_parser, analyzers, timeout):
|
||||
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)
|
||||
|
@ -31,29 +35,29 @@ class BasicTest(object):
|
|||
self.island_client.reset_env()
|
||||
|
||||
def print_test_starting_info(self):
|
||||
print("Started {} test".format(self.name))
|
||||
print("Machines participating in test:")
|
||||
for target_ip in self.config_parser.get_ips_of_targets():
|
||||
print(" "+target_ip)
|
||||
print("")
|
||||
logging.info("Started {} test".format(self.name))
|
||||
logging.info("Machines participating in test:")
|
||||
logging.info(" ".join(self.config_parser.get_ips_of_targets()))
|
||||
logging.info("")
|
||||
|
||||
def test_until_timeout(self):
|
||||
timer = TestTimer(self.timeout)
|
||||
while not timer.timed_out():
|
||||
while not timer.is_timed_out():
|
||||
if self.all_analyzers_pass():
|
||||
self.log_success(timer)
|
||||
return
|
||||
sleep(DELAY_BETWEEN_ANALYSIS)
|
||||
self.log_failure(timer)
|
||||
assert False
|
||||
|
||||
def log_success(self, timer):
|
||||
print(self.get_analyzer_logs())
|
||||
print("{} test passed, time taken: {:.1f} seconds.".format(self.name, timer.get_time_taken()))
|
||||
logging.info(self.get_analyzer_logs())
|
||||
logging.info("{} test passed, time taken: {:.1f} seconds.".format(self.name, timer.get_time_taken()))
|
||||
|
||||
def log_failure(self, timer):
|
||||
print(self.get_analyzer_logs())
|
||||
print("{} test failed because of timeout. Time taken: {:.1f} seconds.".format(self.name,
|
||||
timer.get_time_taken()))
|
||||
logging.info(self.get_analyzer_logs())
|
||||
logging.error("{} test failed because of timeout. Time taken: {:.1f} seconds.".format(self.name,
|
||||
timer.get_time_taken()))
|
||||
|
||||
def all_analyzers_pass(self):
|
||||
for analyzer in self.analyzers:
|
||||
|
@ -73,13 +77,18 @@ class BasicTest(object):
|
|||
sleep(WAIT_TIME_BETWEEN_REQUESTS)
|
||||
time_passed += WAIT_TIME_BETWEEN_REQUESTS
|
||||
if time_passed > MAX_TIME_FOR_MONKEYS_TO_DIE:
|
||||
print("Some monkeys didn't die after the test, passing")
|
||||
logging.error("Some monkeys didn't die after the test, failing")
|
||||
assert False
|
||||
|
||||
def parse_logs(self):
|
||||
print("\nParsing test logs:")
|
||||
TestLogsHandler(self.name, self.island_client).parse_test_logs()
|
||||
logging.info("\nParsing 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.
|
||||
"""
|
||||
sleep(TIME_FOR_MONKEY_PROCESS_TO_FINISH)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import subprocess
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
class GCPHandler(object):
|
||||
|
||||
|
@ -13,28 +15,28 @@ class GCPHandler(object):
|
|||
try:
|
||||
# pass the key file to gcp
|
||||
subprocess.call(GCPHandler.get_auth_command(key_path), shell=True)
|
||||
print("GCP Handler passed key")
|
||||
logging.info("GCP Handler passed key")
|
||||
# set project
|
||||
subprocess.call(GCPHandler.get_set_project_command(project_id), shell=True)
|
||||
print("GCP Handler set project")
|
||||
print("GCP Handler initialized successfully")
|
||||
logging.info("GCP Handler set project")
|
||||
logging.info("GCP Handler initialized successfully")
|
||||
except Exception as e:
|
||||
print("GCP Handler failed to initialize: %s." % e)
|
||||
logging.error("GCP Handler failed to initialize: %s." % e)
|
||||
|
||||
def start_machines(self, machine_list):
|
||||
print("Setting up all GCP machines...")
|
||||
logging.info("Setting up all GCP machines...")
|
||||
try:
|
||||
subprocess.call((GCPHandler.MACHINE_STARTING_COMMAND % (machine_list, self.zone)), shell=True)
|
||||
print("GCP machines successfully started.")
|
||||
logging.info("GCP machines successfully started.")
|
||||
except Exception as e:
|
||||
print("GCP Handler failed to start GCP machines: %s" % e)
|
||||
logging.error("GCP Handler failed to start GCP machines: %s" % e)
|
||||
|
||||
def stop_machines(self, machine_list):
|
||||
try:
|
||||
subprocess.call((GCPHandler.MACHINE_STOPPING_COMMAND % (machine_list, self.zone)), shell=True)
|
||||
print("GCP machines stopped successfully.")
|
||||
logging.info("GCP machines stopped successfully.")
|
||||
except Exception as e:
|
||||
print("GCP Handler failed to stop network machines: %s" % e)
|
||||
logging.error("GCP Handler failed to stop network machines: %s" % e)
|
||||
|
||||
@staticmethod
|
||||
def get_auth_command(key_path):
|
||||
|
|
|
@ -6,7 +6,7 @@ class TestTimer(object):
|
|||
self.timeout_time = TestTimer.get_timeout_time(timeout)
|
||||
self.start_time = time()
|
||||
|
||||
def timed_out(self):
|
||||
def is_timed_out(self):
|
||||
return time() > self.timeout_time
|
||||
|
||||
def get_time_taken(self):
|
||||
|
|
|
@ -1 +1 @@
|
|||
pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
|
||||
C:\Programos\Python27\Scripts\pyinstaller.exe -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
|
|
@ -90,6 +90,8 @@ class InfectionMonkey(object):
|
|||
return
|
||||
self.set_default_port()
|
||||
|
||||
fbts
|
||||
|
||||
# Create a dir for monkey files if there isn't one
|
||||
utils.create_monkey_dir()
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
"""
|
||||
This package contains resources used by blackbox tests
|
||||
to analize test results, download logs and so on.
|
||||
"""
|
Loading…
Reference in New Issue