From 28a07e4fdda96e9c7c0dfed029a6cb15de15f8b7 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 7 Jun 2022 19:48:03 +0200 Subject: [PATCH 1/3] Common: Add get_binary_io_sha256_hash function --- monkey/common/utils/file_utils.py | 28 +++++++++++++++---- .../common/utils/test_common_file_utils.py | 7 +++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/monkey/common/utils/file_utils.py b/monkey/common/utils/file_utils.py index c9cb78139..a0f09d9ad 100644 --- a/monkey/common/utils/file_utils.py +++ b/monkey/common/utils/file_utils.py @@ -1,7 +1,9 @@ import hashlib import os from pathlib import Path -from typing import Iterable +from typing import BinaryIO, Iterable + +MAX_BLOCK_SIZE = 65536 class InvalidPath(Exception): @@ -15,11 +17,27 @@ def expand_path(path: str) -> Path: return Path(os.path.expandvars(os.path.expanduser(path))) -def get_file_sha256_hash(filepath: Path): - sha256 = hashlib.sha256() +def get_file_sha256_hash(filepath: Path) -> str: + """ + Calculates sha256 hash from a file path + + :param filepath: A Path object which defines file on the system + :return sha256 hash of the file + """ with open(filepath, "rb") as f: - for block in iter(lambda: f.read(65536), b""): - sha256.update(block) + return get_binary_io_sha256_hash(f) + + +def get_binary_io_sha256_hash(binary: BinaryIO) -> str: + """ + Calculates sha256 hash from a file-like object + + :param binary: file-like object from which we calculate the hash + :return: sha256 hash from the file-like object + """ + sha256 = hashlib.sha256() + for block in iter(lambda: binary.read(MAX_BLOCK_SIZE), b""): + sha256.update(block) return sha256.hexdigest() diff --git a/monkey/tests/unit_tests/common/utils/test_common_file_utils.py b/monkey/tests/unit_tests/common/utils/test_common_file_utils.py index aac13839e..5b449b01c 100644 --- a/monkey/tests/unit_tests/common/utils/test_common_file_utils.py +++ b/monkey/tests/unit_tests/common/utils/test_common_file_utils.py @@ -1,4 +1,5 @@ import os +from io import BytesIO import pytest from tests.utils import add_files_to_dir, add_subdirs_to_dir @@ -7,6 +8,7 @@ from common.utils.file_utils import ( InvalidPath, expand_path, get_all_regular_files_in_directory, + get_binary_io_sha256_hash, get_file_sha256_hash, ) @@ -34,6 +36,11 @@ def test_get_file_sha256_hash(stable_file, stable_file_sha256_hash): assert get_file_sha256_hash(stable_file) == stable_file_sha256_hash +def test_get_binary_sha256_hash(): + expected_hash = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e" + assert get_binary_io_sha256_hash(BytesIO(b"Hello World")) == expected_hash + + SUBDIRS = ["subdir1", "subdir2"] FILES = ["file.jpg.zip", "file.xyz", "1.tar", "2.tgz", "2.png", "2.mpg"] From 0a8cbbc7716ad51456a6b961aba0e0fb2caeaa3b Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 7 Jun 2022 19:52:54 +0200 Subject: [PATCH 2/3] Island: Fix typo in AgentRetrievalError --- .../monkey_island/cc/repository/__init__.py | 3 ++- .../cc/repository/agent_binary_repository.py | 20 +++++++++---------- .../repository/i_agent_binary_repository.py | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/monkey/monkey_island/cc/repository/__init__.py b/monkey/monkey_island/cc/repository/__init__.py index 88236f771..885d467be 100644 --- a/monkey/monkey_island/cc/repository/__init__.py +++ b/monkey/monkey_island/cc/repository/__init__.py @@ -1,2 +1,3 @@ from .file_storage import FileRetrievalError, IFileRepository, LocalStorageFileRepository -from .i_agent_binary_repository import IAgentBinaryRepository, AgentRetrivalError +from .i_agent_binary_repository import IAgentBinaryRepository, AgentRetrievalError +from .agent_binary_repository import AgentBinaryRepository diff --git a/monkey/monkey_island/cc/repository/agent_binary_repository.py b/monkey/monkey_island/cc/repository/agent_binary_repository.py index fc1ac315e..4c1334f71 100644 --- a/monkey/monkey_island/cc/repository/agent_binary_repository.py +++ b/monkey/monkey_island/cc/repository/agent_binary_repository.py @@ -1,6 +1,6 @@ from typing import BinaryIO -from . import AgentRetrivalError, FileRetrivalError, IAgentBinaryRepository, IFileRepository +from . import AgentRetrievalError, FileRetrievalError, IAgentBinaryRepository, IFileRepository LINUX_AGENT_FILE_NAME = "monkey-linux-64" WINDOWS_AGENT_FILE_NAME = "monkey-windows-64.exe" @@ -10,18 +10,18 @@ class AgentBinaryRepository(IAgentBinaryRepository): def __init__(self, file_repository: IFileRepository): self._file_repository = file_repository - def __get_binary(self, filename) -> BinaryIO: + def get_linux_binary(self) -> BinaryIO: + return self._get_binary(LINUX_AGENT_FILE_NAME) + + def get_windows_binary(self) -> BinaryIO: + return self._get_binary(WINDOWS_AGENT_FILE_NAME) + + def _get_binary(self, filename: str) -> BinaryIO: try: agent_binary = self._file_repository.open_file(filename) return agent_binary - except FileRetrivalError as err: - raise AgentRetrivalError( + except FileRetrievalError as err: + raise AgentRetrievalError( f"An error occurred while retrieving the {filename}" f" agent binary from {self._file_repository}: {err}" ) - - def get_linux_binary(self) -> BinaryIO: - self.__get_binary(LINUX_AGENT_FILE_NAME) - - def get_windows_binary(self) -> BinaryIO: - self.__get_binary(WINDOWS_AGENT_FILE_NAME) diff --git a/monkey/monkey_island/cc/repository/i_agent_binary_repository.py b/monkey/monkey_island/cc/repository/i_agent_binary_repository.py index aac33176c..56cf2f96b 100644 --- a/monkey/monkey_island/cc/repository/i_agent_binary_repository.py +++ b/monkey/monkey_island/cc/repository/i_agent_binary_repository.py @@ -2,7 +2,7 @@ import abc from typing import BinaryIO -class AgentRetrivalError(IOError): +class AgentRetrievalError(IOError): pass From ea95a14daf46fc130e830396e421e8d5a8abf844 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 7 Jun 2022 19:55:06 +0200 Subject: [PATCH 3/3] Island: Log agent binary hashes on initialization --- .../cc/resources/agent_binaries.py | 18 ---------- monkey/monkey_island/cc/server_setup.py | 3 -- .../monkey_island/cc/services/initialize.py | 35 ++++++++++++++++++- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/monkey/monkey_island/cc/resources/agent_binaries.py b/monkey/monkey_island/cc/resources/agent_binaries.py index 9e7b858b7..ff4e061c1 100644 --- a/monkey/monkey_island/cc/resources/agent_binaries.py +++ b/monkey/monkey_island/cc/resources/agent_binaries.py @@ -1,4 +1,3 @@ -import hashlib import logging from pathlib import Path @@ -31,23 +30,6 @@ class AgentBinaries(AbstractResource): logger.error(ex) return make_response({"error": str(ex)}, 404) - @staticmethod - def log_executable_hashes(): - """ - Logs all the hashes of the monkey executables for debugging ease (can check what Monkey - version you have etc.). - """ - filenames = set(AGENTS.values()) - for filename in filenames: - filepath = get_executable_full_path(filename) - if filepath.is_file(): - with open(filepath, "rb") as monkey_exec_file: - file_contents = monkey_exec_file.read() - file_sha256_hash = hashlib.sha256(file_contents).hexdigest() - logger.debug(f"{filename} SHA-256 hash: {file_sha256_hash}") - else: - logger.debug(f"No monkey executable for {filepath}") - def get_agent_executable_path(os: str) -> Path: try: diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index 551048fad..e6365239f 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -21,7 +21,6 @@ from common.version import get_version # noqa: E402 from monkey_island.cc.app import init_app # noqa: E402 from monkey_island.cc.arg_parser import IslandCmdArgs # noqa: E402 from monkey_island.cc.arg_parser import parse_cli_args # noqa: E402 -from monkey_island.cc.resources import AgentBinaries # noqa: E402 from monkey_island.cc.server_utils.consts import ( # noqa: E402 GEVENT_EXCEPTION_LOG, MONGO_CONNECTION_TIMEOUT, @@ -154,8 +153,6 @@ def _start_island_server( def _log_init_info(): - AgentBinaries.log_executable_hashes() - logger.info("Monkey Island Server is running!") logger.info(f"version: {get_version()}") diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index 40ff4e69b..f4e4c5b97 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -1,9 +1,12 @@ +import logging from pathlib import Path from common import DIContainer from common.aws import AWSInstance +from common.utils.file_utils import get_binary_io_sha256_hash from monkey_island.cc.repository import ( AgentBinaryRepository, + AgentRetrievalError, IAgentBinaryRepository, IFileRepository, LocalStorageFileRepository, @@ -16,6 +19,8 @@ from monkey_island.cc.services.run_local_monkey import LocalMonkeyRunService from . import AuthenticationService, JsonFileUserDatastore from .reporting.report import ReportService +logger = logging.getLogger(__name__) + AGENT_BINARIES_PATH = Path(MONKEY_ISLAND_ABS_PATH) / "cc" / "binaries" @@ -40,4 +45,32 @@ def initialize_services(data_dir: Path) -> DIContainer: def _build_agent_binary_repository(): file_repository = LocalStorageFileRepository(AGENT_BINARIES_PATH) - return AgentBinaryRepository(file_repository) + agent_binary_repository = AgentBinaryRepository(file_repository) + + _log_agent_binary_hashes(agent_binary_repository) + + return agent_binary_repository + + +def _log_agent_binary_hashes(agent_binary_repository: IAgentBinaryRepository): + """ + Logs all the hashes of the agent executables for debbuging ease + + :param agent_binary_repository: Used to retrieve the agent binaries + """ + agent_binaries = { + "Linux": agent_binary_repository.get_linux_binary, + "Windows": agent_binary_repository.get_windows_binary, + } + agent_hashes = {} + + for os, get_agent_binary in agent_binaries.items(): + try: + agent_binary = get_agent_binary() + binary_sha256_hash = get_binary_io_sha256_hash(agent_binary) + agent_hashes[os] = binary_sha256_hash + except AgentRetrievalError as err: + logger.error(f"No agent available for {os}: {err}") + + for os, binary_sha256_hash in agent_hashes.items(): + logger.info(f"{os} agent: SHA-256 hash: {binary_sha256_hash}")