From 51406f3c863f0446b0937d87dcb43b9dc939c291 Mon Sep 17 00:00:00 2001 From: D3vil0p3r Date: Tue, 12 Nov 2024 01:15:38 +0100 Subject: [PATCH 1/4] Fix fetch submodule check --- empire/server/server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/empire/server/server.py b/empire/server/server.py index bfeb69cd2..6fcc88738 100755 --- a/empire/server/server.py +++ b/empire/server/server.py @@ -144,6 +144,9 @@ def check_submodules(): def fetch_submodules(): + if not os.path.exists(Path(".git")): + log.info("No .git directory found. Skipping submodule fetch.") + return command = ["git", "submodule", "update", "--init", "--recursive"] run_as_user(command) From 70db094f665db09fa605f2f9dead7625b03192fd Mon Sep 17 00:00:00 2001 From: D3vil0p3r Date: Tue, 12 Nov 2024 15:33:50 +0100 Subject: [PATCH 2/4] Config files management --- empire.py | 5 +- empire/client/src/EmpireCliConfig.py | 6 +- empire/config_manager.py | 85 ++++++++++++++++++++++++++++ empire/server/core/config.py | 4 +- empire/server/server.py | 4 +- 5 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 empire/config_manager.py diff --git a/empire.py b/empire.py index a9687dec4..300e4495c 100644 --- a/empire.py +++ b/empire.py @@ -3,9 +3,12 @@ import sys from empire import arguments +from empire import config_manager + if __name__ == "__main__": args = arguments.args + config_manager.config_init() if args.subparser_name == "server": from empire.server import server @@ -16,7 +19,7 @@ from empire.scripts.sync_starkiller import sync_starkiller - with open("empire/server/config.yaml") as f: + with open(config_manager.CONFIG_SERVER_PATH) as f: config = yaml.safe_load(f) sync_starkiller(config) diff --git a/empire/client/src/EmpireCliConfig.py b/empire/client/src/EmpireCliConfig.py index 10efeaa28..63084472a 100644 --- a/empire/client/src/EmpireCliConfig.py +++ b/empire/client/src/EmpireCliConfig.py @@ -3,6 +3,8 @@ import yaml +from empire import config_manager + log = logging.getLogger(__name__) @@ -15,7 +17,8 @@ def __init__(self): self.set_yaml(location) if len(self.yaml.items()) == 0: log.info("Loading default config") - self.set_yaml("./empire/client/config.yaml") + self.set_yaml(config_manager.CONFIG_CLIENT_PATH) + config_manager.check_config_permission(self.yaml, "client") def set_yaml(self, location: str): try: @@ -26,5 +29,4 @@ def set_yaml(self, location: str): except FileNotFoundError as exc: log.error(exc) - empire_config = EmpireCliConfig() diff --git a/empire/config_manager.py b/empire/config_manager.py new file mode 100644 index 000000000..e8ff9caed --- /dev/null +++ b/empire/config_manager.py @@ -0,0 +1,85 @@ +import logging +import os +import shutil +import yaml + +from pathlib import Path + +log = logging.getLogger(__name__) + +user_home = Path.home() +SOURCE_CONFIG_CLIENT = Path("empire/client/config.yaml") +SOURCE_CONFIG_SERVER = Path("empire/server/config.yaml") +CONFIG_DIR = user_home / ".empire" +CONFIG_CLIENT_PATH = CONFIG_DIR / "client" / "config.yaml" +CONFIG_SERVER_PATH = CONFIG_DIR / "server" / "config.yaml" + +def config_init(): + CONFIG_CLIENT_PATH.parent.mkdir(parents=True, exist_ok=True) + CONFIG_SERVER_PATH.parent.mkdir(parents=True, exist_ok=True) + + if not CONFIG_CLIENT_PATH.exists(): + shutil.copy(SOURCE_CONFIG_CLIENT, CONFIG_CLIENT_PATH) + log.info(f"Copied {SOURCE_CONFIG_CLIENT} to {CONFIG_CLIENT_PATH}") + else: + log.info(f"{CONFIG_CLIENT_PATH} already exists.") + + if not CONFIG_SERVER_PATH.exists(): + shutil.copy(SOURCE_CONFIG_SERVER, CONFIG_SERVER_PATH) + log.info(f"Copied {SOURCE_CONFIG_SERVER} to {CONFIG_SERVER_PATH}") + else: + log.info(f"{CONFIG_SERVER_PATH} already exists.") + + +def check_config_permission(config_dict: dict, config_type: str): + """ + Check if the specified directories in config.yaml are writable. If not, switches to a fallback directory. + Handles both server and client configurations. + + Args: + config_dict (dict): The configuration dictionary loaded from YAML. + config_type (str): The type of configuration ("server" or "client"). + """ + # Define paths to check based on config type + if config_type == "server": + paths_to_check = { + ("api", "cert_path"): config_dict["api"]["cert_path"], + ("database", "sqlite", "location"): config_dict["database"]["sqlite"]["location"], + ("starkiller", "directory"): config_dict["starkiller"]["directory"], + ("logging", "directory"): config_dict["logging"]["directory"], + ("debug", "last_task", "file"): config_dict["debug"]["last_task"]["file"], + ("directories", "downloads"): config_dict["directories"].get("downloads"), + } + config_path = CONFIG_SERVER_PATH # Use the server config path + + elif config_type == "client": + paths_to_check = { + ("logging", "directory"): config_dict["logging"]["directory"], + ("directories", "downloads"): config_dict["directories"].get("downloads"), + ("directories", "generated-stagers"): config_dict["directories"].get("generated-stagers"), + } + config_path = CONFIG_CLIENT_PATH # Use the client config path + + else: + raise ValueError("Invalid config_type. Expected 'server' or 'client'.") + + # Check permissions and update paths as needed + for keys, dir_path in paths_to_check.items(): + if not os.access(dir_path, os.W_OK): + log.info("No write permission for %s. Switching to fallback directory.", dir_path) + user_home = Path.home() + fallback_dir = os.path.join(user_home, ".empire", dir_path.removeprefix("empire/")) + + # Update the directory in config_dict + target = config_dict # target is a reference to config_dict + for key in keys[:-1]: + target = target[key] + target[keys[-1]] = fallback_dir + + log.info("Updated %s to fallback directory: %s", "->".join(keys), fallback_dir) + + # Write the updated configuration back to the correct YAML file + with open(config_path, 'w') as config_file: + yaml.safe_dump(config_dict, config_file) + + log.info("Updated $config_type config.yaml to use fallback directory: %s", config_type, fallback_dir) \ No newline at end of file diff --git a/empire/server/core/config.py b/empire/server/core/config.py index 539c56c87..a1bd8566c 100644 --- a/empire/server/core/config.py +++ b/empire/server/core/config.py @@ -3,6 +3,7 @@ from pathlib import Path import yaml +from empire import config_manager from pydantic import BaseModel, ConfigDict, Field, field_validator log = logging.getLogger(__name__) @@ -133,6 +134,7 @@ def set_yaml(location: str): config_dict = set_yaml(location) if len(config_dict.items()) == 0: log.info("Loading default config") - config_dict = set_yaml("./empire/server/config.yaml") + config_dict = set_yaml(config_manager.CONFIG_SERVER_PATH) +config_manager.check_config_permission(config_dict, "server") empire_config = EmpireConfig(config_dict) diff --git a/empire/server/server.py b/empire/server/server.py index 6fcc88738..b7c9f63b7 100755 --- a/empire/server/server.py +++ b/empire/server/server.py @@ -70,7 +70,9 @@ def setup_logging(args): INVOKE_OBFS_SRC_DIR_BASE = os.path.join( os.path.dirname(__file__), "data/Invoke-Obfuscation" ) -INVOKE_OBFS_DST_DIR_BASE = "/usr/local/share/powershell/Modules/Invoke-Obfuscation" + +user_home = Path.home() +INVOKE_OBFS_DST_DIR_BASE = user_home / ".local" / "share" / "powershell" / "Modules" / "Invoke-Obfuscation" def reset(): From 01db305cc372ba348a3680613c03a35e381be7f6 Mon Sep 17 00:00:00 2001 From: D3vil0p3r Date: Thu, 21 Nov 2024 11:08:38 +0100 Subject: [PATCH 3/4] Remove Invoke-Obfuscation --- .github/cst-config-docker.yaml | 3 -- .../cst-config-install-base.yaml | 3 -- Dockerfile | 4 +- empire.py | 4 +- empire/client/src/EmpireCliConfig.py | 1 + empire/config_manager.py | 37 +++++++++++++------ empire/server/core/config.py | 3 +- empire/server/server.py | 17 --------- setup/install.sh | 3 -- 9 files changed, 31 insertions(+), 44 deletions(-) diff --git a/.github/cst-config-docker.yaml b/.github/cst-config-docker.yaml index 195bdef20..7f268c26f 100644 --- a/.github/cst-config-docker.yaml +++ b/.github/cst-config-docker.yaml @@ -48,9 +48,6 @@ fileExistenceTests: - name: 'profiles' path: '/empire/empire/server/data/profiles/' shouldExist: true - - name: 'invoke obfuscation' - path: '/usr/local/share/powershell/Modules/Invoke-Obfuscation/' - shouldExist: true - name: 'sharpire' path: '/empire/empire/server/csharp/Covenant/Data/ReferenceSourceLibraries/Sharpire' shouldExist: true diff --git a/.github/install_tests/cst-config-install-base.yaml b/.github/install_tests/cst-config-install-base.yaml index d79acc82a..61bcbb77d 100644 --- a/.github/install_tests/cst-config-install-base.yaml +++ b/.github/install_tests/cst-config-install-base.yaml @@ -71,9 +71,6 @@ fileExistenceTests: - name: 'profiles' path: '/empire/empire/server/data/profiles/' shouldExist: true - - name: 'invoke obfuscation' - path: '/usr/local/share/powershell/Modules/Invoke-Obfuscation/' - shouldExist: true - name: 'sharpire' path: '/empire/empire/server/csharp/Covenant/Data/ReferenceSourceLibraries/Sharpire' shouldExist: true diff --git a/Dockerfile b/Dockerfile index 61fdf3e43..65fd89ca9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,9 +59,7 @@ RUN poetry config virtualenvs.create false && \ COPY . /empire -RUN mkdir -p /usr/local/share/powershell/Modules && \ - cp -r ./empire/server/data/Invoke-Obfuscation /usr/local/share/powershell/Modules && \ - rm -rf /empire/empire/server/data/empire* +RUN rm -rf /empire/empire/server/data/empire* RUN sed -i 's/use: mysql/use: sqlite/g' empire/server/config.yaml && \ sed -i 's/auto_update: true/auto_update: false/g' empire/server/config.yaml diff --git a/empire.py b/empire.py index 300e4495c..9c199f719 100644 --- a/empire.py +++ b/empire.py @@ -2,9 +2,7 @@ import sys -from empire import arguments -from empire import config_manager - +from empire import arguments, config_manager if __name__ == "__main__": args = arguments.args diff --git a/empire/client/src/EmpireCliConfig.py b/empire/client/src/EmpireCliConfig.py index 63084472a..ec8476c6f 100644 --- a/empire/client/src/EmpireCliConfig.py +++ b/empire/client/src/EmpireCliConfig.py @@ -29,4 +29,5 @@ def set_yaml(self, location: str): except FileNotFoundError as exc: log.error(exc) + empire_config = EmpireCliConfig() diff --git a/empire/config_manager.py b/empire/config_manager.py index e8ff9caed..a1c8d34ee 100644 --- a/empire/config_manager.py +++ b/empire/config_manager.py @@ -1,10 +1,10 @@ import logging import os import shutil -import yaml - from pathlib import Path +import yaml + log = logging.getLogger(__name__) user_home = Path.home() @@ -14,10 +14,11 @@ CONFIG_CLIENT_PATH = CONFIG_DIR / "client" / "config.yaml" CONFIG_SERVER_PATH = CONFIG_DIR / "server" / "config.yaml" + def config_init(): CONFIG_CLIENT_PATH.parent.mkdir(parents=True, exist_ok=True) CONFIG_SERVER_PATH.parent.mkdir(parents=True, exist_ok=True) - + if not CONFIG_CLIENT_PATH.exists(): shutil.copy(SOURCE_CONFIG_CLIENT, CONFIG_CLIENT_PATH) log.info(f"Copied {SOURCE_CONFIG_CLIENT} to {CONFIG_CLIENT_PATH}") @@ -35,7 +36,7 @@ def check_config_permission(config_dict: dict, config_type: str): """ Check if the specified directories in config.yaml are writable. If not, switches to a fallback directory. Handles both server and client configurations. - + Args: config_dict (dict): The configuration dictionary loaded from YAML. config_type (str): The type of configuration ("server" or "client"). @@ -44,7 +45,9 @@ def check_config_permission(config_dict: dict, config_type: str): if config_type == "server": paths_to_check = { ("api", "cert_path"): config_dict["api"]["cert_path"], - ("database", "sqlite", "location"): config_dict["database"]["sqlite"]["location"], + ("database", "sqlite", "location"): config_dict["database"]["sqlite"][ + "location" + ], ("starkiller", "directory"): config_dict["starkiller"]["directory"], ("logging", "directory"): config_dict["logging"]["directory"], ("debug", "last_task", "file"): config_dict["debug"]["last_task"]["file"], @@ -56,7 +59,9 @@ def check_config_permission(config_dict: dict, config_type: str): paths_to_check = { ("logging", "directory"): config_dict["logging"]["directory"], ("directories", "downloads"): config_dict["directories"].get("downloads"), - ("directories", "generated-stagers"): config_dict["directories"].get("generated-stagers"), + ("directories", "generated-stagers"): config_dict["directories"].get( + "generated-stagers" + ), } config_path = CONFIG_CLIENT_PATH # Use the client config path @@ -66,9 +71,13 @@ def check_config_permission(config_dict: dict, config_type: str): # Check permissions and update paths as needed for keys, dir_path in paths_to_check.items(): if not os.access(dir_path, os.W_OK): - log.info("No write permission for %s. Switching to fallback directory.", dir_path) + log.info( + "No write permission for %s. Switching to fallback directory.", dir_path + ) user_home = Path.home() - fallback_dir = os.path.join(user_home, ".empire", dir_path.removeprefix("empire/")) + fallback_dir = os.path.join( + user_home, ".empire", dir_path.removeprefix("empire/") + ) # Update the directory in config_dict target = config_dict # target is a reference to config_dict @@ -76,10 +85,16 @@ def check_config_permission(config_dict: dict, config_type: str): target = target[key] target[keys[-1]] = fallback_dir - log.info("Updated %s to fallback directory: %s", "->".join(keys), fallback_dir) + log.info( + "Updated %s to fallback directory: %s", "->".join(keys), fallback_dir + ) # Write the updated configuration back to the correct YAML file - with open(config_path, 'w') as config_file: + with open(config_path, "w") as config_file: yaml.safe_dump(config_dict, config_file) - log.info("Updated $config_type config.yaml to use fallback directory: %s", config_type, fallback_dir) \ No newline at end of file + log.info( + "Updated %s config.yaml to use fallback directory: %s", + config_type, + fallback_dir, + ) diff --git a/empire/server/core/config.py b/empire/server/core/config.py index a1bd8566c..1a358dca9 100644 --- a/empire/server/core/config.py +++ b/empire/server/core/config.py @@ -3,9 +3,10 @@ from pathlib import Path import yaml -from empire import config_manager from pydantic import BaseModel, ConfigDict, Field, field_validator +from empire import config_manager + log = logging.getLogger(__name__) diff --git a/empire/server/server.py b/empire/server/server.py index b7c9f63b7..01287e692 100755 --- a/empire/server/server.py +++ b/empire/server/server.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import logging import os -import pathlib import pwd import shutil import signal @@ -67,12 +66,6 @@ def setup_logging(args): CSHARP_DIR_BASE = os.path.join(os.path.dirname(__file__), "csharp/Covenant") -INVOKE_OBFS_SRC_DIR_BASE = os.path.join( - os.path.dirname(__file__), "data/Invoke-Obfuscation" -) - -user_home = Path.home() -INVOKE_OBFS_DST_DIR_BASE = user_home / ".local" / "share" / "powershell" / "Modules" / "Invoke-Obfuscation" def reset(): @@ -96,16 +89,6 @@ def reset(): if os.path.exists(empire_config.starkiller.directory): shutil.rmtree(empire_config.starkiller.directory) - # invoke obfuscation - if os.path.exists(f"{INVOKE_OBFS_DST_DIR_BASE}"): - shutil.rmtree(INVOKE_OBFS_DST_DIR_BASE) - pathlib.Path(pathlib.Path(INVOKE_OBFS_SRC_DIR_BASE).parent).mkdir( - parents=True, exist_ok=True - ) - shutil.copytree( - INVOKE_OBFS_SRC_DIR_BASE, INVOKE_OBFS_DST_DIR_BASE, dirs_exist_ok=True - ) - file_util.remove_file("data/sessions.csv") file_util.remove_file("data/credentials.csv") file_util.remove_file("data/master.log") diff --git a/setup/install.sh b/setup/install.sh index 7eeeace3c..28c3c4c6a 100755 --- a/setup/install.sh +++ b/setup/install.sh @@ -40,9 +40,6 @@ function install_powershell() { sudo tar zxf /tmp/powershell.tar.gz -C /opt/microsoft/powershell/7 sudo chmod +x /opt/microsoft/powershell/7/pwsh sudo ln -s /opt/microsoft/powershell/7/pwsh /usr/bin/pwsh - - sudo mkdir -p /usr/local/share/powershell/Modules - sudo cp -r "$PARENT_PATH"/empire/server/data/Invoke-Obfuscation /usr/local/share/powershell/Modules } function install_mysql() { From aca6df6ad0a7fc576d66e751e2cdf76ed03edac5 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 22 Nov 2024 12:39:10 +0100 Subject: [PATCH 4/4] Update CHANGELOG and code --- CHANGELOG.md | 4 ++ docs/quickstart/configuration/client.md | 2 + docs/quickstart/configuration/server.md | 2 + empire/client/src/EmpireCliConfig.py | 2 +- empire/config_manager.py | 64 +++++++++++++++++-------- empire/server/core/config.py | 43 +++++++++++------ empire/server/modules/bof/nanodump.py | 3 +- empire/test/test_zz_reset.py | 8 +--- 8 files changed, 82 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d592319d..9ec54468e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Support Empire for system-wide deployment (@D3vil0p3r) +- Paths specified in config.yaml where user does not have write permission will be fallback to ~/.empire directory and config.yaml updated as well (@D3vil0p3r) +- Invoke-Obfuscation is no longer copied to /usr/local/share + ## [5.11.7] - 2024-11-11 - Fix arm installs by installing dotnet and powershell manually diff --git a/docs/quickstart/configuration/client.md b/docs/quickstart/configuration/client.md index 22d0f294f..393f10ee9 100644 --- a/docs/quickstart/configuration/client.md +++ b/docs/quickstart/configuration/client.md @@ -5,6 +5,8 @@ The Client configuration is managed via [empire/client/config.yaml](https://github.com/BC-SECURITY/Empire/blob/master/empire/client/config.yaml). +Once launched, Empire checks for user write permissions on paths specified in `config.yaml`. If the current user does not have write permissions on these paths, `~/.empire` will be set as fallback parent directory and the configuration file will be updated as well. + * **servers** - The servers block is meant to give the user the ability to set up frequently used Empire servers. If a server is listed in this block then when connecting to the server they need only type: `connect -c localhost`. diff --git a/docs/quickstart/configuration/server.md b/docs/quickstart/configuration/server.md index e6c0a9c44..4e00381ee 100644 --- a/docs/quickstart/configuration/server.md +++ b/docs/quickstart/configuration/server.md @@ -2,6 +2,8 @@ The Server configuration is managed via [empire/server/config.yaml](https://github.com/BC-SECURITY/Empire/blob/master/empire/client/config.yaml). +Once launched, Empire checks for user write permissions on paths specified in `config.yaml`. If the current user does not have write permissions on these paths, `~/.empire` will be set as fallback parent directory and the configuration file will be updated as well. + * **suppress-self-cert-warning** - Suppress the http warnings when launching an Empire instance that uses a self-signed cert. * **api** - Configure the RESTful API. This includes the port to run the API on, as well as the path for the SSL certificates. If `empire-priv.key` and `empire-chain.pem` are not found in this directory, self-signed certs will be generated. diff --git a/empire/client/src/EmpireCliConfig.py b/empire/client/src/EmpireCliConfig.py index ec8476c6f..9029ebb71 100644 --- a/empire/client/src/EmpireCliConfig.py +++ b/empire/client/src/EmpireCliConfig.py @@ -18,7 +18,7 @@ def __init__(self): if len(self.yaml.items()) == 0: log.info("Loading default config") self.set_yaml(config_manager.CONFIG_CLIENT_PATH) - config_manager.check_config_permission(self.yaml, "client") + config_manager.check_config_permission(self.yaml, "client") def set_yaml(self, location: str): try: diff --git a/empire/config_manager.py b/empire/config_manager.py index a1c8d34ee..5f172133c 100644 --- a/empire/config_manager.py +++ b/empire/config_manager.py @@ -44,24 +44,32 @@ def check_config_permission(config_dict: dict, config_type: str): # Define paths to check based on config type if config_type == "server": paths_to_check = { - ("api", "cert_path"): config_dict["api"]["cert_path"], - ("database", "sqlite", "location"): config_dict["database"]["sqlite"][ - "location" - ], - ("starkiller", "directory"): config_dict["starkiller"]["directory"], - ("logging", "directory"): config_dict["logging"]["directory"], - ("debug", "last_task", "file"): config_dict["debug"]["last_task"]["file"], - ("directories", "downloads"): config_dict["directories"].get("downloads"), + ("api", "cert_path"): config_dict.get("api", {}).get("cert_path"), + ("database", "sqlite", "location"): config_dict.get("database", {}) + .get("sqlite", {}) + .get("location"), + ("starkiller", "directory"): config_dict.get("starkiller", {}).get( + "directory" + ), + ("logging", "directory"): config_dict.get("logging", {}).get("directory"), + ("debug", "last_task", "file"): config_dict.get("debug", {}) + .get("last_task", {}) + .get("file"), + ("directories", "downloads"): config_dict.get("directories", {}).get( + "downloads" + ), } config_path = CONFIG_SERVER_PATH # Use the server config path elif config_type == "client": paths_to_check = { - ("logging", "directory"): config_dict["logging"]["directory"], - ("directories", "downloads"): config_dict["directories"].get("downloads"), - ("directories", "generated-stagers"): config_dict["directories"].get( - "generated-stagers" + ("logging", "directory"): config_dict.get("logging", {}).get("directory"), + ("directories", "downloads"): config_dict.get("directories", {}).get( + "downloads" ), + ("directories", "generated-stagers"): config_dict.get( + "directories", {} + ).get("generated-stagers"), } config_path = CONFIG_CLIENT_PATH # Use the client config path @@ -70,13 +78,21 @@ def check_config_permission(config_dict: dict, config_type: str): # Check permissions and update paths as needed for keys, dir_path in paths_to_check.items(): - if not os.access(dir_path, os.W_OK): + if dir_path is None: + continue + + current_dir = dir_path + while current_dir and not os.path.exists(current_dir): + current_dir = os.path.dirname(current_dir) + + if not os.access(current_dir, os.W_OK): log.info( - "No write permission for %s. Switching to fallback directory.", dir_path + "No write permission for %s. Switching to fallback directory.", + current_dir, ) user_home = Path.home() fallback_dir = os.path.join( - user_home, ".empire", dir_path.removeprefix("empire/") + user_home, ".empire", str(current_dir).removeprefix("empire/") ) # Update the directory in config_dict @@ -91,10 +107,16 @@ def check_config_permission(config_dict: dict, config_type: str): # Write the updated configuration back to the correct YAML file with open(config_path, "w") as config_file: - yaml.safe_dump(config_dict, config_file) + yaml.safe_dump(paths2str(config_dict), config_file) + + return config_dict + - log.info( - "Updated %s config.yaml to use fallback directory: %s", - config_type, - fallback_dir, - ) +def paths2str(data): + if isinstance(data, dict): + return {key: paths2str(value) for key, value in data.items()} + if isinstance(data, list): + return [paths2str(item) for item in data] + if isinstance(data, Path): + return str(data) + return data diff --git a/empire/server/core/config.py b/empire/server/core/config.py index 1a358dca9..46eea3f51 100644 --- a/empire/server/core/config.py +++ b/empire/server/core/config.py @@ -76,9 +76,9 @@ def __getitem__(self, key): class DirectoriesConfig(EmpireBaseModel): - downloads: Path - module_source: Path - obfuscated_module_source: Path + downloads: Path = Path("empire/server/downloads") + module_source: Path = Path("empire/server/modules") + obfuscated_module_source: Path = Path("empire/server/data/obfuscated_module_source") class LoggingConfig(EmpireBaseModel): @@ -101,17 +101,26 @@ class EmpireConfig(EmpireBaseModel): alias="supress-self-cert-warning", default=True ) api: ApiConfig | None = ApiConfig() - starkiller: StarkillerConfig - submodules: SubmodulesConfig - database: DatabaseConfig + starkiller: StarkillerConfig = StarkillerConfig() + submodules: SubmodulesConfig = SubmodulesConfig() + database: DatabaseConfig = DatabaseConfig( + sqlite=SQLiteDatabaseConfig(), + mysql=MySQLDatabaseConfig(), + defaults=DatabaseDefaultsConfig(), + ) plugins: dict[str, dict[str, str]] = {} - directories: DirectoriesConfig - logging: LoggingConfig - debug: DebugConfig + directories: DirectoriesConfig = DirectoriesConfig() + logging: LoggingConfig = LoggingConfig() + debug: DebugConfig = DebugConfig(last_task=LastTaskConfig()) model_config = ConfigDict(extra="allow") - def __init__(self, config_dict: dict): + def __init__(self, config_dict: dict | None = None): + if config_dict is None: + config_dict = {} + if not isinstance(config_dict, dict): + raise ValueError("config_dict must be a dictionary") + super().__init__(**config_dict) # For backwards compatibility self.yaml = config_dict @@ -128,14 +137,18 @@ def set_yaml(location: str): log.warning(exc) -config_dict = {} +config_dict = EmpireConfig().model_dump() if "--config" in sys.argv: location = sys.argv[sys.argv.index("--config") + 1] log.info(f"Loading config from {location}") - config_dict = set_yaml(location) -if len(config_dict.items()) == 0: + loaded_config = set_yaml(location) + if loaded_config: + config_dict = loaded_config +elif config_manager.CONFIG_SERVER_PATH.exists(): log.info("Loading default config") - config_dict = set_yaml(config_manager.CONFIG_SERVER_PATH) + loaded_config = set_yaml(config_manager.CONFIG_SERVER_PATH) + if loaded_config: + config_dict = loaded_config + config_dict = config_manager.check_config_permission(config_dict, "server") -config_manager.check_config_permission(config_dict, "server") empire_config = EmpireConfig(config_dict) diff --git a/empire/server/modules/bof/nanodump.py b/empire/server/modules/bof/nanodump.py index 30d635115..c214234b8 100644 --- a/empire/server/modules/bof/nanodump.py +++ b/empire/server/modules/bof/nanodump.py @@ -16,8 +16,7 @@ def generate( module=module, params=params, obfuscate=obfuscate ) - for name in params: - value = params[name] + for name, value in params.items(): if name == "write": if value != "": dump_path = value diff --git a/empire/test/test_zz_reset.py b/empire/test/test_zz_reset.py index a3c7dbfe1..da047377d 100644 --- a/empire/test/test_zz_reset.py +++ b/empire/test/test_zz_reset.py @@ -39,8 +39,6 @@ def test_reset_server(monkeypatch, tmp_path, default_argv, server_config_dict): 1. Deletes the sqlite db. Don't need to test mysql atm. 2. Deletes the downloads dir contents 3. Deletes the csharp generated files - 4. Deletes the obfuscated modules - 5. Deletes / Copies invoke obfuscation """ monkeypatch.setattr("builtins.input", lambda _: "y") sys.argv = [*default_argv.copy(), "--reset"] @@ -64,9 +62,8 @@ def test_reset_server(monkeypatch, tmp_path, default_argv, server_config_dict): for f in download_files: assert Path(downloads_dir + f[0]).exists() - # Change the csharp and Invoke-Obfuscation dir so we don't delete real files. + # Change the csharp dir so we don't delete real files. csharp_dir = tmp_path / "empire/server/data/csharp" - invoke_obfs_dir = tmp_path / "powershell/Modules/Invoke-Obfuscation" # Write files to csharp_dir csharp_files = [ @@ -105,7 +102,6 @@ def test_reset_server(monkeypatch, tmp_path, default_argv, server_config_dict): assert Path(server_config_dict["database"]["location"]).exists() server.CSHARP_DIR_BASE = csharp_dir - server.INVOKE_OBFS_DST_DIR_BASE = invoke_obfs_dir with pytest.raises(SystemExit): server.run(args) @@ -126,8 +122,6 @@ def test_reset_server(monkeypatch, tmp_path, default_argv, server_config_dict): csharp_dir / "Data/Tasks/CSharp/Compiled/netcoreapp3.0" / f[0] ).exists() - assert Path(invoke_obfs_dir / "Invoke-Obfuscation.ps1").exists() - if server_config_dict.get("database", {}).get("type") == "sqlite": assert not Path(server_config_dict["database"]["location"]).exists()