diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f87f54e..871b9923 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [4.0.0] = TBC ### Added +- Log observation space data by episode and step. +- Added `show_history` method to Agents, allowing you to view actions taken by an agent per step. By default, `do-nothing` actions are omitted. +- New ``node-send-local-command`` action implemented which grants agents the ability to execute commands locally. (Previously limited to remote only) +- Added ability to set the observation threshold for NMNE, file access and application executions ### Changed - Agents now follow a common configuration format, simplifying the configuration of agents and their extensibilty. @@ -24,6 +28,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated tests that don't use YAMLs to still use the new action and agent schemas - Nodes now use a config schema and are extensible, allowing for plugin support. - Node tests have been updated to use the new node config schemas when not using YAML files. +- ACLs are no longer applied to layer-2 traffic. +- Random number seed values are recorded in simulation/seed.log if the seed is set in the config file + or `generate_seed_value` is set to `true`. +- ARP .show() method will now include the port number associated with each entry. +- Added `services_requires_scan` and `applications_requires_scan` to agent observation space config to allow the agents to be able to see actual health states of services and applications without requiring scans (Default `True`, set to `False` to allow agents to see actual health state without scanning). +- Updated the `Terminal` class to provide response information when sending remote command execution. ### Fixed - DNS client no longer fails to check its cache if a DNS server address is missing. diff --git a/docs/Makefile b/docs/Makefile index 99e19d16..d0f9af01 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,4 +1,4 @@ -# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK # Minimal makefile for Sphinx documentation # You can set these variables from the command line, and also # from the environment for the first two. diff --git a/docs/build-sphinx-docs-to-github-pages.sh b/docs/build-sphinx-docs-to-github-pages.sh index 9e43a2b4..a180f168 100644 --- a/docs/build-sphinx-docs-to-github-pages.sh +++ b/docs/build-sphinx-docs-to-github-pages.sh @@ -1,4 +1,4 @@ -# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK #!/bin/bash set -x diff --git a/docs/source/configuration/agents.rst b/docs/source/configuration/agents.rst index dce8da3a..ee84aede 100644 --- a/docs/source/configuration/agents.rst +++ b/docs/source/configuration/agents.rst @@ -21,7 +21,7 @@ Agents can be scripted (deterministic and stochastic), or controlled by a reinfo team: GREEN type: probabilistic-agent observation_space: - type: UC2GreenObservation + type: UC2GreenObservation # TODO: what action_space: reward_function: reward_components: @@ -160,3 +160,4 @@ If ``True``, gymnasium flattening will be performed on the observation space bef ----------------- Agents will record their action log for each step. This is a summary of what the agent did, along with response information from requests within the simulation. +A summary of the actions taken by the agent can be viewed using the `show_history()` function. By default, this will display all actions taken apart from ``DONOTHING``. diff --git a/docs/source/configuration/simulation/nodes/common/common_node_attributes.rst b/docs/source/configuration/simulation/nodes/common/common_node_attributes.rst index 542b817b..e6d5da67 100644 --- a/docs/source/configuration/simulation/nodes/common/common_node_attributes.rst +++ b/docs/source/configuration/simulation/nodes/common/common_node_attributes.rst @@ -54,6 +54,39 @@ Optional. Default value is ``3``. The number of time steps required to occur in order for the node to cycle from ``ON`` to ``SHUTTING_DOWN`` and then finally ``OFF``. +``file_system`` +--------------- + +Optional. + +The file system of the node. This configuration allows nodes to be initialised with files and/or folders. + +The file system takes a list of folders and files. + +Example: + +.. code-block:: yaml + + simulation: + network: + nodes: + - hostname: client_1 + type: computer + ip_address: 192.168.10.11 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.10.1 + file_system: + - empty_folder # example of an empty folder + - downloads: + - "test_1.txt" # files in the downloads folder + - "test_2.txt" + - root: + - passwords: # example of file with size and type + size: 69 # size in bytes + type: TXT # See FileType for list of available file types + +List of file types: :py:mod:`primaite.simulator.file_system.file_type.FileType` + ``users`` --------- diff --git a/docs/source/configuration/simulation/nodes/network_examples.rst b/docs/source/configuration/simulation/nodes/network_examples.rst index 4616139e..84ee4c60 100644 --- a/docs/source/configuration/simulation/nodes/network_examples.rst +++ b/docs/source/configuration/simulation/nodes/network_examples.rst @@ -1177,8 +1177,8 @@ ACLs permitting or denying traffic as per our configured ACL rules. some_tech_storage_srv = network.get_node_by_hostname("some_tech_storage_srv") some_tech_storage_srv.file_system.create_file(file_name="test.png") - pc_1_ftp_client: FTPClient = network.get_node_by_hostname("pc_1").software_manager.software["FTPClient"] - pc_2_ftp_client: FTPClient = network.get_node_by_hostname("pc_2").software_manager.software["FTPClient"] + pc_1_ftp_client: FTPClient = network.get_node_by_hostname("pc_1").software_manager.software["ftp-client"] + pc_2_ftp_client: FTPClient = network.get_node_by_hostname("pc_2").software_manager.software["ftp-client"] assert not pc_1_ftp_client.request_file( dest_ip_address=some_tech_storage_srv.network_interface[1].ip_address, @@ -1224,7 +1224,7 @@ ACLs permitting or denying traffic as per our configured ACL rules. web_server: Server = network.get_node_by_hostname("some_tech_web_srv") - web_ftp_client: FTPClient = web_server.software_manager.software["FTPClient"] + web_ftp_client: FTPClient = web_server.software_manager.software["ftp-client"] assert not web_ftp_client.request_file( dest_ip_address=some_tech_storage_srv.network_interface[1].ip_address, @@ -1269,7 +1269,7 @@ ACLs permitting or denying traffic as per our configured ACL rules. some_tech_storage_srv.file_system.create_file(file_name="test.png") some_tech_snr_dev_pc: Computer = network.get_node_by_hostname("some_tech_snr_dev_pc") - snr_dev_ftp_client: FTPClient = some_tech_snr_dev_pc.software_manager.software["FTPClient"] + snr_dev_ftp_client: FTPClient = some_tech_snr_dev_pc.software_manager.software["ftp-client"] assert snr_dev_ftp_client.request_file( dest_ip_address=some_tech_storage_srv.network_interface[1].ip_address, @@ -1294,7 +1294,7 @@ ACLs permitting or denying traffic as per our configured ACL rules. some_tech_storage_srv.file_system.create_file(file_name="test.png") some_tech_jnr_dev_pc: Computer = network.get_node_by_hostname("some_tech_jnr_dev_pc") - jnr_dev_ftp_client: FTPClient = some_tech_jnr_dev_pc.software_manager.software["FTPClient"] + jnr_dev_ftp_client: FTPClient = some_tech_jnr_dev_pc.software_manager.software["ftp-client"] assert not jnr_dev_ftp_client.request_file( dest_ip_address=some_tech_storage_srv.network_interface[1].ip_address, @@ -1337,7 +1337,7 @@ ACLs permitting or denying traffic as per our configured ACL rules. some_tech_storage_srv.file_system.create_file(file_name="test.png") some_tech_hr_pc: Computer = network.get_node_by_hostname("some_tech_hr_1") - hr_ftp_client: FTPClient = some_tech_hr_pc.software_manager.software["FTPClient"] + hr_ftp_client: FTPClient = some_tech_hr_pc.software_manager.software["ftp-client"] assert not hr_ftp_client.request_file( dest_ip_address=some_tech_storage_srv.network_interface[1].ip_address, diff --git a/docs/source/configuration/simulation/nodes/router.rst b/docs/source/configuration/simulation/nodes/router.rst index 4b41784c..ee278a98 100644 --- a/docs/source/configuration/simulation/nodes/router.rst +++ b/docs/source/configuration/simulation/nodes/router.rst @@ -74,7 +74,7 @@ The subnet mask setting for the port. ``acl`` ------- -Sets up the ACL rules for the router. +Sets up the ACL rules for the router to apply to layer-3 traffic. These are not applied to layer-2 traffic such as ARP. e.g. @@ -85,10 +85,6 @@ e.g. ... acl: 1: - action: PERMIT - src_port: ARP - dst_port: ARP - 2: action: PERMIT protocol: ICMP diff --git a/docs/source/how_to_guides/extensible_agents.rst b/docs/source/how_to_guides/extensible_agents.rst index 1d765417..3236c21a 100644 --- a/docs/source/how_to_guides/extensible_agents.rst +++ b/docs/source/how_to_guides/extensible_agents.rst @@ -46,17 +46,13 @@ The core features that should be implemented in any new agent are detailed below - ref: example_green_agent team: GREEN - type: ExampleAgent + type: example-agent action_space: action_map: 0: action: do-nothing options: {} - reward_function: - reward_components: - - type: dummy - agent_settings: start_step: 25 frequency: 20 diff --git a/docs/source/how_to_guides/extensible_nodes.rst b/docs/source/how_to_guides/extensible_nodes.rst index 043d0f06..18d64ca8 100644 --- a/docs/source/how_to_guides/extensible_nodes.rst +++ b/docs/source/how_to_guides/extensible_nodes.rst @@ -26,9 +26,9 @@ class Router(NetworkNode, identifier="router"): """ Represents a network router within the simulation, managing routing and forwarding of IP packets across network interfaces.""" SYSTEM_SOFTWARE: ClassVar[Dict] = { - "UserSessionManager": UserSessionManager, - "UserManager": UserManager, - "Terminal": Terminal, + "user-session-manager": UserSessionManager, + "user-manager": UserManager, + "terminal": Terminal, } network_interfaces: Dict[str, RouterInterface] = {} @@ -52,4 +52,4 @@ class Router(NetworkNode, identifier="router"): Changes to YAML file. ===================== -While effort has been made to ensure that nodes defined within configuration YAML files for use with PrimAITE 3.X remain compatible with PrimAITE v4+, it is encouraged to review for minor changes needed. +While effort has been made to ensure that nodes defined within configuration YAML files for use with PrimAITE 3.X remain compatible with PrimAITE v4+, it is encouraged to review for minor changes needed. diff --git a/docs/source/request_system.rst b/docs/source/request_system.rst index f0437705..93fc2a9f 100644 --- a/docs/source/request_system.rst +++ b/docs/source/request_system.rst @@ -2,6 +2,8 @@ © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +.. _request_system: + Request System ************** diff --git a/docs/source/simulation_components/network/network.rst b/docs/source/simulation_components/network/network.rst index 152b74b8..a6fe4070 100644 --- a/docs/source/simulation_components/network/network.rst +++ b/docs/source/simulation_components/network/network.rst @@ -97,19 +97,19 @@ we'll use the following Network that has a client, server, two switches, and a r network.connect(endpoint_a=switch_2.network_interface[1], endpoint_b=client_1.network_interface[1]) network.connect(endpoint_a=switch_1.network_interface[1], endpoint_b=server_1.network_interface[1]) -8. Add ACL rules on the Router to allow ARP and ICMP traffic. +8. Add an ACL rule on the Router to allow ICMP traffic. .. code-block:: python router_1.acl.add_rule( action=ACLAction.PERMIT, - src_port=Port["ARP"], - dst_port=Port["ARP"], + src_port=PORT_LOOKUP["ARP"], + dst_port=PORT_LOOKUP["ARP"], position=22 ) router_1.acl.add_rule( action=ACLAction.PERMIT, - protocol=IPProtocol["ICMP"], + protocol=PROTOCOL_LOOKUP["ICMP"], position=23 ) diff --git a/docs/source/simulation_components/network/nodes/wireless_router.rst b/docs/source/simulation_components/network/nodes/wireless_router.rst index d7207846..4078ffda 100644 --- a/docs/source/simulation_components/network/nodes/wireless_router.rst +++ b/docs/source/simulation_components/network/nodes/wireless_router.rst @@ -102,8 +102,8 @@ ICMP traffic, ensuring basic network connectivity and ping functionality. network.connect(pc_a.network_interface[1], router_1.router_interface) # Configure Router 1 ACLs - router_1.acl.add_rule(action=ACLAction.PERMIT, src_port=Port["ARP"], dst_port=Port["ARP"], position=22) - router_1.acl.add_rule(action=ACLAction.PERMIT, protocol=IPProtocol["ICMP"], position=23) + router_1.acl.add_rule(action=ACLAction.PERMIT, src_port=PORT_LOOKUP["ARP"], dst_port=PORT_LOOKUP["ARP"], position=22) + router_1.acl.add_rule(action=ACLAction.PERMIT, protocol=PROTOCOL_LOOKUP["ICMP"], position=23) # Configure PC B pc_b = Computer( diff --git a/docs/source/simulation_components/system/applications/c2_suite.rst b/docs/source/simulation_components/system/applications/c2_suite.rst index 34175fc3..c780485a 100644 --- a/docs/source/simulation_components/system/applications/c2_suite.rst +++ b/docs/source/simulation_components/system/applications/c2_suite.rst @@ -183,7 +183,7 @@ Python # Example command: Installing and configuring Ransomware: ransomware_installation_command = { "commands": [ - ["software_manager","application","install","RansomwareScript"], + ["software_manager","application","install","ransomware-script"], ], "username": "admin", "password": "admin", diff --git a/docs/source/simulation_components/system/applications/data_manipulation_bot.rst b/docs/source/simulation_components/system/applications/data_manipulation_bot.rst index 8e008504..3ddb8bca 100644 --- a/docs/source/simulation_components/system/applications/data_manipulation_bot.rst +++ b/docs/source/simulation_components/system/applications/data_manipulation_bot.rst @@ -77,7 +77,7 @@ Python network.connect(endpoint_b=client_1.network_interface[1], endpoint_a=switch_2.network_interface[1]) client_1.software_manager.install(DatabaseClient) client_1.software_manager.install(DataManipulationBot) - data_manipulation_bot: DataManipulationBot = client_1.software_manager.software.get("DataManipulationBot") + data_manipulation_bot: DataManipulationBot = client_1.software_manager.software.get("data-manipulation-bot") data_manipulation_bot.configure(server_ip_address=IPv4Address("192.168.1.14"), payload="DELETE") data_manipulation_bot.run() @@ -98,7 +98,7 @@ If not using the data manipulation bot manually, it needs to be used with a data type: red-database-corrupting-agent observation_space: - type: UC2RedObservation + type: uc2-red-observation #TODO what options: nodes: - node_name: client_1 diff --git a/docs/source/simulation_components/system/applications/database_client.rst b/docs/source/simulation_components/system/applications/database_client.rst index 7087dedf..472b504c 100644 --- a/docs/source/simulation_components/system/applications/database_client.rst +++ b/docs/source/simulation_components/system/applications/database_client.rst @@ -59,7 +59,7 @@ Python # install DatabaseClient client.software_manager.install(DatabaseClient) - database_client: DatabaseClient = client.software_manager.software.get("DatabaseClient") + database_client: DatabaseClient = client.software_manager.software.get("database-sclient") # Configure the DatabaseClient database_client.configure(server_ip_address=IPv4Address("192.168.0.1")) # address of the DatabaseService diff --git a/docs/source/simulation_components/system/applications/ransomware_script.rst b/docs/source/simulation_components/system/applications/ransomware_script.rst index 192618fc..a8975f32 100644 --- a/docs/source/simulation_components/system/applications/ransomware_script.rst +++ b/docs/source/simulation_components/system/applications/ransomware_script.rst @@ -62,7 +62,7 @@ Python network.connect(endpoint_b=client_1.network_interface[1], endpoint_a=switch_2.network_interface[1]) client_1.software_manager.install(DatabaseClient) client_1.software_manager.install(RansomwareScript) - RansomwareScript: RansomwareScript = client_1.software_manager.software.get("RansomwareScript") + RansomwareScript: RansomwareScript = client_1.software_manager.software.get("ransomware-script") RansomwareScript.configure(server_ip_address=IPv4Address("192.168.1.14")) RansomwareScript.execute() diff --git a/docs/source/simulation_components/system/applications/web_browser.rst b/docs/source/simulation_components/system/applications/web_browser.rst index c04e60af..659caa09 100644 --- a/docs/source/simulation_components/system/applications/web_browser.rst +++ b/docs/source/simulation_components/system/applications/web_browser.rst @@ -61,7 +61,7 @@ The :ref:`DNSClient` must be configured to use the :ref:`DNSServer`. The :ref:`D # Install WebBrowser on computer computer.software_manager.install(WebBrowser) - web_browser: WebBrowser = computer.software_manager.software.get("WebBrowser") + web_browser: WebBrowser = computer.software_manager.software.get("web-browser") web_browser.run() # configure the WebBrowser diff --git a/docs/source/simulation_components/system/services/database_service.rst b/docs/source/simulation_components/system/services/database_service.rst index 961f2e45..c819a0f7 100644 --- a/docs/source/simulation_components/system/services/database_service.rst +++ b/docs/source/simulation_components/system/services/database_service.rst @@ -66,7 +66,7 @@ Python # Install DatabaseService on server server.software_manager.install(DatabaseService) - db_service: DatabaseService = server.software_manager.software.get("DatabaseService") + db_service: DatabaseService = server.software_manager.software.get("database-service") db_service.start() # configure DatabaseService diff --git a/docs/source/simulation_components/system/services/dns_client.rst b/docs/source/simulation_components/system/services/dns_client.rst index 17a1ed25..40762bfc 100644 --- a/docs/source/simulation_components/system/services/dns_client.rst +++ b/docs/source/simulation_components/system/services/dns_client.rst @@ -56,7 +56,7 @@ Python # Install DNSClient on server server.software_manager.install(DNSClient) - dns_client: DNSClient = server.software_manager.software.get("DNSClient") + dns_client: DNSClient = server.software_manager.software.get("dns-client") dns_client.start() # configure DatabaseService diff --git a/docs/source/simulation_components/system/services/dns_server.rst b/docs/source/simulation_components/system/services/dns_server.rst index 633221d5..ca0e3691 100644 --- a/docs/source/simulation_components/system/services/dns_server.rst +++ b/docs/source/simulation_components/system/services/dns_server.rst @@ -53,7 +53,7 @@ Python # Install DNSServer on server server.software_manager.install(DNSServer) - dns_server: DNSServer = server.software_manager.software.get("DNSServer") + dns_server: DNSServer = server.software_manager.software.get("dns-server") dns_server.start() # configure DatabaseService diff --git a/docs/source/simulation_components/system/services/ftp_client.rst b/docs/source/simulation_components/system/services/ftp_client.rst index d4375069..530b5aff 100644 --- a/docs/source/simulation_components/system/services/ftp_client.rst +++ b/docs/source/simulation_components/system/services/ftp_client.rst @@ -60,7 +60,7 @@ Python # Install FTPClient on server server.software_manager.install(FTPClient) - ftp_client: FTPClient = server.software_manager.software.get("FTPClient") + ftp_client: FTPClient = server.software_manager.software.get("ftp-client") ftp_client.start() diff --git a/docs/source/simulation_components/system/services/ftp_server.rst b/docs/source/simulation_components/system/services/ftp_server.rst index a5ad32fe..20dd6707 100644 --- a/docs/source/simulation_components/system/services/ftp_server.rst +++ b/docs/source/simulation_components/system/services/ftp_server.rst @@ -55,7 +55,7 @@ Python # Install FTPServer on server server.software_manager.install(FTPServer) - ftp_server: FTPServer = server.software_manager.software.get("FTPServer") + ftp_server: FTPServer = server.software_manager.software.get("ftp-server") ftp_server.start() ftp_server.server_password = "test" diff --git a/docs/source/simulation_components/system/services/ntp_client.rst b/docs/source/simulation_components/system/services/ntp_client.rst index 8c011cad..5406d9fc 100644 --- a/docs/source/simulation_components/system/services/ntp_client.rst +++ b/docs/source/simulation_components/system/services/ntp_client.rst @@ -53,7 +53,7 @@ Python # Install NTPClient on server server.software_manager.install(NTPClient) - ntp_client: NTPClient = server.software_manager.software.get("NTPClient") + ntp_client: NTPClient = server.software_manager.software.get("ntp-client") ntp_client.start() ntp_client.configure(ntp_server_ip_address=IPv4Address("192.168.0.10")) diff --git a/docs/source/simulation_components/system/services/ntp_server.rst b/docs/source/simulation_components/system/services/ntp_server.rst index c1d16d61..2c01dcaf 100644 --- a/docs/source/simulation_components/system/services/ntp_server.rst +++ b/docs/source/simulation_components/system/services/ntp_server.rst @@ -55,7 +55,7 @@ Python # Install NTPServer on server server.software_manager.install(NTPServer) - ntp_server: NTPServer = server.software_manager.software.get("NTPServer") + ntp_server: NTPServer = server.software_manager.software.get("ntp-server") ntp_server.start() diff --git a/docs/source/simulation_components/system/services/terminal.rst b/docs/source/simulation_components/system/services/terminal.rst index bc5cee48..5c9bad79 100644 --- a/docs/source/simulation_components/system/services/terminal.rst +++ b/docs/source/simulation_components/system/services/terminal.rst @@ -23,6 +23,14 @@ Key capabilities - Simulates common Terminal processes/commands. - Leverages the Service base class for install/uninstall, status tracking etc. +Usage +""""" + + - Pre-Installs on any `Node` component (with the exception of `Switches`). + - Terminal Clients connect, execute commands and disconnect from remote nodes. + - Ensures that users are logged in to the component before executing any commands. + - Service runs on SSH port 22 by default. + - Enables Agents to send commands both remotely and locally. Implementation """""""""""""" @@ -30,19 +38,112 @@ Implementation - Manages remote connections in a dictionary by session ID. - Processes commands, forwarding to the ``RequestManager`` or ``SessionManager`` where appropriate. - Extends Service class. - - A detailed guide on the implementation and functionality of the Terminal class can be found in the "Terminal-Processing" jupyter notebook. + +A detailed guide on the implementation and functionality of the Terminal class can be found in the "Terminal-Processing" jupyter notebook. + +Command Format +^^^^^^^^^^^^^^ + +Terminals implement their commands through leveraging the pre-existing :ref:`request_system`. + +Due to this Terminals will only accept commands passed within the ``RequestFormat``. + +:py:class:`primaite.game.interface.RequestFormat` + +For example, ``terminal`` command actions when used in ``yaml`` format are formatted as follows: + +.. code-block:: yaml + + command: + - "file_system" + - "create" + - "file" + - "downloads" + - "cat.png" + - "False + +This is then loaded from yaml into a dictionary containing the terminal command: + +.. code-block:: python + + {"command":["file_system", "create", "file", "downloads", "cat.png", "False"]} + +Which is then passed to the ``Terminals`` Request Manager to be executed. + +Game Layer Usage (Agents) +======================== + +The below code examples demonstrate how to use terminal related actions in yaml files. + +yaml +"""" + +``node-send-local-command`` +""""""""""""""""""""""""""" + +Agents can execute local commands without needing to perform a separate remote login action (``node-session-remote-login``). + +.. code-block:: yaml + + ... + ... + action: node-send-local-command + options: + node_id: 0 + username: admin + password: admin + command: # Example command - Creates a file called 'cat.png' in the downloads folder. + - "file_system" + - "create" + - "file" + - "downloads" + - "cat.png" + - "False" -Usage -""""" +``node-session-remote-login`` +""""""""""""""""" - - Pre-Installs on all ``Nodes`` (with the exception of ``Switches``). - - Terminal Clients connect, execute commands and disconnect from remote nodes. - - Ensures that users are logged in to the component before executing any commands. - - Service runs on SSH port 22 by default. +Agents are able to use the terminal to login into remote nodes via ``SSH`` which allows for agents to execute commands on remote hosts. + +.. code-block:: yaml + + ... + ... + action: node-session-remote-login + options: + node_id: 0 + username: admin + password: admin + remote_ip: 192.168.0.10 # Example Ip Address. (The remote host's IP that will be used by ssh) + + +``node-send-remote-command`` +"""""""""""""""""""""""""""" + +After remotely logging into another host, an agent can use the ``node-send-remote-command`` to execute commands across the network remotely. + +.. code-block:: yaml + + ... + ... + action: node-send-remote-command + options: + node_id: 0 + remote_ip: 192.168.0.10 + command: + - "file_system" + - "create" + - "file" + - "downloads" + - "cat.png" + - "False" + + + +Simulation Layer Usage +====================== -Usage -===== The below code examples demonstrate how to create a terminal, a remote terminal, and how to send a basic application install command to a remote node. @@ -65,7 +166,7 @@ Python operating_state=NodeOperatingState.ON, ) - terminal: Terminal = client.software_manager.software.get("Terminal") + terminal: Terminal = client.software_manager.software.get("terminal") Creating Remote Terminal Connection """"""""""""""""""""""""""""""""""" @@ -86,7 +187,7 @@ Creating Remote Terminal Connection node_b.power_on() network.connect(node_a.network_interface[1], node_b.network_interface[1]) - terminal_a: Terminal = node_a.software_manager.software.get("Terminal") + terminal_a: Terminal = node_a.software_manager.software.get("terminal") term_a_term_b_remote_connection: RemoteTerminalConnection = terminal_a.login(username="admin", password="Admin123!", ip_address="192.168.0.11") @@ -112,12 +213,12 @@ Executing a basic application install command node_b.power_on() network.connect(node_a.network_interface[1], node_b.network_interface[1]) - terminal_a: Terminal = node_a.software_manager.software.get("Terminal") + terminal_a: Terminal = node_a.software_manager.software.get("terminal") term_a_term_b_remote_connection: RemoteTerminalConnection = terminal_a.login(username="admin", password="Admin123!", ip_address="192.168.0.11") - term_a_term_b_remote_connection.execute(["software_manager", "application", "install", "RansomwareScript"]) + term_a_term_b_remote_connection.execute(["software_manager", "application", "install", "ransomware-script"]) @@ -140,7 +241,7 @@ Creating a folder on a remote node node_b.power_on() network.connect(node_a.network_interface[1], node_b.network_interface[1]) - terminal_a: Terminal = node_a.software_manager.software.get("Terminal") + terminal_a: Terminal = node_a.software_manager.software.get("terminal") term_a_term_b_remote_connection: RemoteTerminalConnection = terminal_a.login(username="admin", password="Admin123!", ip_address="192.168.0.11") @@ -167,7 +268,7 @@ Disconnect from Remote Node node_b.power_on() network.connect(node_a.network_interface[1], node_b.network_interface[1]) - terminal_a: Terminal = node_a.software_manager.software.get("Terminal") + terminal_a: Terminal = node_a.software_manager.software.get("terminal") term_a_term_b_remote_connection: RemoteTerminalConnection = terminal_a.login(username="admin", password="Admin123!", ip_address="192.168.0.11") diff --git a/docs/source/simulation_components/system/services/web_server.rst b/docs/source/simulation_components/system/services/web_server.rst index bce42791..9d7f4d2f 100644 --- a/docs/source/simulation_components/system/services/web_server.rst +++ b/docs/source/simulation_components/system/services/web_server.rst @@ -56,7 +56,7 @@ Python # Install WebServer on server server.software_manager.install(WebServer) - web_server: WebServer = server.software_manager.software.get("WebServer") + web_server: WebServer = server.software_manager.software.get("web-server") web_server.start() Via Configuration diff --git a/docs/source/simulation_components/system/software.rst b/docs/source/simulation_components/system/software.rst index d28815bb..c2f3066b 100644 --- a/docs/source/simulation_components/system/software.rst +++ b/docs/source/simulation_components/system/software.rst @@ -30,7 +30,7 @@ See :ref:`Node Start up and Shut down` node.software_manager.install(WebServer) - web_server: WebServer = node.software_manager.software.get("WebServer") + web_server: WebServer = node.software_manager.software.get("web-server") assert web_server.operating_state is ServiceOperatingState.RUNNING # service is immediately ran after install node.power_off() diff --git a/src/primaite/config/_package_data/uc7_config.yaml b/src/primaite/config/_package_data/uc7_config.yaml new file mode 100644 index 00000000..2a665e46 --- /dev/null +++ b/src/primaite/config/_package_data/uc7_config.yaml @@ -0,0 +1,3253 @@ +########################################################## +# USE CASE 7 CONFIGURATION YAML FILE # +########################################################## + + +########################################## +# PrimAITE Game and Logging Settings # +########################################## + + +# PrimAITE I/O Settings # + +io_settings: + save_agent_actions: false + save_step_metadata: false + save_pcap_logs: false + save_sys_logs: True + # save_sys_logs: true + write_sys_log_to_terminal: false + + +# PrimAITE Game Settings # + +game: + max_episode_length: 128 + ports: + - FTP + - DNS + - HTTP + - NTP + - POSTGRES_SERVER + - SSH + protocols: + - ICMP + - TCP + - UDP + thresholds: + nmne: + high: 10 + medium: 5 + low: 0 + +############################################ +# PrimAITE Use Case 7 Simulation # +############################################ + +########################################## +# Configuration Variables (Yaml Anchors) # +########################################## + +# External Network Address List # + +DNS_SUBNET: &DNS_SUBNET 255.255.255.240 # | 8.8.8.0 / 28 +HOME_INTERNET_SUBNET: &HOME_INTERNET_SUBNET 255.255.255.252 # | 10.1.0.0 / 30 +REMOTE_INTERNET_SUBNET: &REMOTE_INTERNET_SUBNET 255.255.255.252 # | 10.1.10.0 / 30 +SOME_TECH_INTERNET_SUBNET: &ST_INTERNET_SUBNET 255.255.255.252 # | 10.1.100.0 / 30 +HOME_OFFICE_SUBNET: &HOME_SUBNET 255.255.255.0 # | 192.168.1.0 / 26 +REMOTE_SUBNET_DMZ: &REMOTE_SUBNET_DMZ 255.255.255.252 # | 192.168.10.0 / 30 +REMOTE_SUBNET: &REMOTE_SUBNET 255.255.255.240 # | 192.168.20.0 / 28 + +# SOME_TECH (ST) Network Address List # + +SOME_TECH_DMZ_SUBNET: &ST_DMZ_SUBNET 255.255.255.252 # | 192.168.100.0 / 30 +SOME_TECH_INTRANET_RT_CR_SUBNET: &ST_INTRA_CR_SUBNET 255.255.255.240 # | 192.168.150.0 / 28 +SOME_TECH_INTRANET_RT_DR_ONE_SUBNET: &ST_INTRA_DR_ONE_SUBNET 255.255.255.252 # | 192.168.160.0 / 30 +SOME_TECH_INTRANET_RT_DR_TWO_SUBNET: &ST_INTRA_DR_TWO_SUBNET 255.255.255.252 # | 192.168.170.0 / 30 +SOME_TECH_HEAD_OFFICE_SUBNET: &ST_HO_SUBNET 255.255.255.248 # | 192.168.200.0 / 29 +SOME_TECH_HUMAN_RESOURCES_SUBNET: &ST_HR_SUBNET 255.255.255.248 # | 192.168.210.0 / 29 +SOME_TECH_DATA_SUBNET: &ST_DATA_SUBNET 255.255.255.248 # | 192.168.220.0 / 29 +SOME_TECH_PROJECT_A_SUBNET: &ST_PROJ_A_SUBNET 255.255.255.248 # | 192.168.230.0 / 29 +SOME_TECH_PROJECT_B_SUBNET: &ST_PROJ_B_SUBNET 255.255.255.248 # | 192.168.240.0 / 29 +SOME_TECH_PROJECT_C_SUBNET: &ST_PROJ_C_SUBNET 255.255.255.248 # | 192.168.250.0 / 29 + +# Host & Server Configurations # + +# ST Public Web Server | web-server | ST-DMZ-PUB-SRV-WEB +SOME_TECH_PUBLIC_SERVER_WEB_IP_ADDRESS: &ST_PUB_SRV_WEB_IP 192.168.100.2 +SOME_TECH_PUBLIC_SERVER_WEB_CONFIG: &ST_SRV_WEB_CONFIG # + - type: web-server + +# ISP Public DNS | dns-server | ISP-PUB-SRV-DNS +PUBLIC_DNS_IP_ADDRESS: &PUBLIC_DNS_IP 8.8.8.8 +PUBLIC_DNS_CONFIG: &PUBLIC_DNS_CONFIG # + - type: dns-server + options: + domain_mapping: + some_tech.com: *ST_PUB_SRV_WEB_IP + +# ST Private Storage Server | ftp-server | ST-DATA-PRV-SRV-STORAGE +SOME_TECH_PRIVATE_SERVER_STORAGE_IP: &ST_SRV_STORAGE_IP 192.168.220.2 +SOME_TECH_PRIVATE_SERVER_STORAGE_CONFIG: &ST_SRV_STORAGE_CONFIG + - type: ftp-server + +# ST Private Database Server | database-client & ftp-client | ST-DATA-PRV-SRV-DB +SOME_TECH_PRIVATE_SERVER_DATABASE_IP: &ST_SRV_DB_IP 192.168.220.3 +SOME_TECH_PRIVATE_SERVER_DATABASE_CONFIG: &ST_SRV_DB_CONFIG + - type: database-service + options: + backup_server_ip: *ST_SRV_STORAGE_IP + - type: ftp-client + +# Default PC Configuration | Database Client & Web Server +PERSONAL_COMPUTER_DEFAULT_CONFIG: &PC_DEFAULT_CONFIG + - type: database-client + options: + db_server_ip: *ST_SRV_DB_IP + - type: web-browser + options: + target_url: http://some_tech.com + + +############################## +# Simulation Configuration # +############################## + +simulation: + defaults: + folder_scan_duration: 0 + folder_restore_duration: 3 + service_fix_duration: 2 + service_restart_duration: 2 + software_install_duration: 0 + node_start_up_duration: 3 + node_shut_down_duration: 3 + node_scan_duration: 8 + network: + nmne_config: + capture_nmne: true + nmne_capture_keywords: + - DELETE + - ENCRYPT + nodes: + ###################### + # HOME OFFICE SUBNET # + ###################### + - hostname: HOME-PUB-RT-DR + type: router + default_gateway: 10.1.0.1 + ports: + 1: + ip_address: 192.168.1.1 + subnet_mask: *HOME_SUBNET + 2: + ip_address: 10.1.0.2 + subnet_mask: *HOME_INTERNET_SUBNET + default_route: + next_hop_ip_address: 10.1.0.1 + acl: + 5: + action: PERMIT + + - hostname: HOME-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: HOME-PUB-PC-1 + type: computer + ip_address: 192.168.1.2 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-PC-2 + type: computer + ip_address: 192.168.1.3 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-SRV + type: server + ip_address: 192.168.1.4 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + + ###################### + # INTERNET SUBNET # + ###################### + - hostname: ISP-PUB-RT-BR + type: router + ports: + 1: + ip_address: 10.1.0.1 + subnet_mask: *HOME_INTERNET_SUBNET + 2: + ip_address: 8.8.8.1 + subnet_mask: *DNS_SUBNET + 3: + ip_address: 10.1.10.1 + subnet_mask: *REMOTE_INTERNET_SUBNET + 4: + ip_address: 10.1.100.1 + subnet_mask: *ST_INTERNET_SUBNET + routes: + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.0.2 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 8.8.8.8 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.10.2 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.10.2 + + default_route: + next_hop_ip_address: 10.1.100.2 # SOME_TECH Firewall + + acl: + 5: + action: PERMIT + + ################ + # DNS SUBNET # + ################ + - hostname: ISP-PUB-SRV-DNS + type: server + ip_address: 8.8.8.8 + subnet_mask: *DNS_SUBNET + default_gateway: 8.8.8.1 + services: + *PUBLIC_DNS_CONFIG + applications: + - type: c2-server # Represents the external internet. + options: + listen_on_ports: + - 80 + - 53 + - 21 + ######################## + # REMOTE SITE SUBNET # + ######################## + - hostname: REM-PUB-FW + type: firewall + ports: + external_port: # Public Internet facing + ip_address: 10.1.10.2 + subnet_mask: *REMOTE_INTERNET_SUBNET + internal_port: # Remote Site (DMZ Subnet) facing + ip_address: 192.168.10.1 + subnet_mask: *REMOTE_SUBNET_DMZ + routes: + - address: 192.168.20.0 # Remote Site Network + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 192.168.10.2 + default_route: + next_hop_ip_address: 10.1.10.1 # Forward to internet router port 3 + acl: # TODO: Expand on this (For now permit all) + internal_inbound_acl: + 1: + action: PERMIT + internal_outbound_acl: + 1: + action: PERMIT + dmz_inbound_acl: + 1: + action: PERMIT + dmz_outbound_acl: + 1: + action: PERMIT + external_inbound_acl: + 1: + action: PERMIT + external_inbound_acl: + 1: + action: PERMIT + + - hostname: REM-PUB-RT-DR + type: router + default_gateway: 192.168.10.1 + ports: + 1: + ip_address: 192.168.10.2 + subnet_mask: *REMOTE_SUBNET_DMZ + 2: + ip_address: 192.168.20.1 + subnet_mask: *REMOTE_SUBNET + default_route: + next_hop_ip_address: 192.168.10.1 + acl: + 5: + action: PERMIT + + - hostname: REM-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: REM-PUB-PC-1 + type: computer + ip_address: 192.168.20.2 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-PC-2 + type: computer + ip_address: 192.168.20.3 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-SRV + type: server + ip_address: 192.168.20.4 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + + + ######################## + # SOME_TECH DMZ SUBNET # + ######################## + + - hostname: ST-PUB-FW + type: firewall + ports: + external_port: # Public Internet Facing Port + ip_address: 10.1.100.2 + subnet_mask: *ST_INTERNET_SUBNET + internal_port: # SOME_TECH Intranet Port + ip_address: 192.168.150.1 + subnet_mask: *ST_INTRA_CR_SUBNET + dmz_port: # SOME_TECH Port Facing Port + ip_address: 192.168.100.1 + subnet_mask: *ST_DMZ_SUBNET + acl: # TODO: Expand on this (For now permit all) + internal_inbound_acl: + 5: + action: PERMIT + internal_outbound_acl: + 5: + action: PERMIT + dmz_inbound_acl: + 5: + action: PERMIT + dmz_outbound_acl: + 5: + action: PERMIT + external_inbound_acl: + 5: + action: PERMIT + external_inbound_acl: + 5: + action: PERMIT + routes: + - address: 10.1.100.0 + subnet_mask: *ST_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.10.0 + subnet_mask: *REMOTE_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.0.0 + subnet_mask: *HOME_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 10.1.100.1 + + # - address: 192.168.150.0 + # subnet_mask: *ST_INTRA_CR_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.160.0 + # subnet_mask: *ST_INTRA_DR_ONE_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.170.0 + # subnet_mask: *ST_INTRA_DR_TWO_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.200.0 + # subnet_mask: *ST_HO_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.210.0 + # subnet_mask: *ST_HR_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.220.0 + # subnet_mask: *ST_DATA_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.230.0 + # subnet_mask: *ST_PROJ_A_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.240.0 + # subnet_mask: *ST_PROJ_B_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.250.0 + # subnet_mask: *ST_PROJ_C_SUBNET + # next_hop_ip_address: 192.168.150.2 + default_route: + #next_hop_ip_address: 10.1.100.1 + next_hop_ip_address: 192.168.150.2 + + - hostname: ST-DMZ-PUB-SRV-WEB + type: server + ip_address: *ST_PUB_SRV_WEB_IP + subnet_mask: *ST_DMZ_SUBNET + default_gateway: 192.168.100.1 + services: + *ST_SRV_WEB_CONFIG + + ############################# + # SOME_TECH INTRANET SUBNET # + ############################# + + - hostname: ST-INTRA-PRV-RT-CR + type: router + ports: + 1: + ip_address: 192.168.150.2 + subnet_mask: *ST_INTRA_CR_SUBNET + 2: + ip_address: 192.168.160.1 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 3: + ip_address: 192.168.170.1 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 4: + ip_address: 192.168.220.1 + subnet_mask: *ST_DATA_SUBNET + + routes: + - address: 192.168.200.0 + subnet_mask: *ST_HO_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.210.0 + subnet_mask: *ST_HR_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.230.0 + subnet_mask: *ST_PROJ_A_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.240.0 + subnet_mask: *ST_PROJ_B_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.250.0 + subnet_mask: *ST_PROJ_C_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + + default_route: + next_hop_ip_address: 192.168.150.1 # ST Public Firewall Internal Port + acl: + 5: + action: PERMIT + + - hostname: ST-INTRA-PRV-RT-DR-1 + type: router + ports: + 1: + ip_address: 192.168.160.2 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 2: + ip_address: 192.168.230.1 + subnet_mask: *ST_PROJ_A_SUBNET + 3: + ip_address: 192.168.240.1 + subnet_mask: *ST_PROJ_B_SUBNET + 4: + ip_address: 192.168.250.1 + subnet_mask: *ST_PROJ_C_SUBNET + default_route: + next_hop_ip_address: 192.168.160.1 # ST Intranet CR Router Port 2 + acl: + 5: + action: PERMIT + + - hostname: ST-INTRA-PRV-RT-DR-2 + type: router + default_gateway: 192.168.170.1 + ports: + 1: + ip_address: 192.168.170.2 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 2: + ip_address: 192.168.200.1 + subnet_mask: *ST_HO_SUBNET + 3: + ip_address: 192.168.210.1 + subnet_mask: *ST_HR_SUBNET + default_route: + next_hop_ip_address: 192.168.170.1 # ST Intranet CR Router Port 3 + acl: + 5: + action: PERMIT + + ################################ + # SOME_TECH HEAD OFFICE SUBNET # + ################################ + + - hostname: ST-HO-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-HO-PRV-PC-1 + type: computer + ip_address: 192.168.200.2 + default_gateway: 192.168.200.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HO-PRV-PC-2 + type: computer + ip_address: 192.168.200.3 + default_gateway: 192.168.200.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HO-PRV-PC-3 + type: computer + ip_address: 192.168.200.4 + default_gateway: 192.168.200.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + #################################### + # SOME_TECH HUMAN RESOURCES SUBNET # + #################################### + + - hostname: ST-HR-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-HR-PRV-PC-1 + type: computer + ip_address: 192.168.210.2 + default_gateway: 192.168.210.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HR-PRV-PC-2 + type: computer + ip_address: 192.168.210.3 + default_gateway: 192.168.210.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HR-PRV-PC-3 + type: computer + ip_address: 192.168.210.4 + default_gateway: 192.168.210.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ######################### + # SOME_TECH DATA SUBNET # + ######################### + + - hostname: ST-DATA-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-DATA-PRV-SRV-STORAGE + type: server + ip_address: *ST_SRV_STORAGE_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_STORAGE_CONFIG + + - hostname: ST-DATA-PRV-SRV-DB + type: server + ip_address: *ST_SRV_DB_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_DB_CONFIG + + ####################### + # SOME_TECH PROJECT A # + ####################### + + - hostname: ST-PROJ-A-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-PROJ-A-PRV-PC-1 + type: computer + ip_address: 192.168.230.2 + default_gateway: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-A-PRV-PC-2 + type: computer + ip_address: 192.168.230.3 + default_gateway: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-A-PRV-PC-3 + type: computer + ip_address: 192.168.230.4 + default_gateway: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT B # + ####################### + + - hostname: ST-PROJ-B-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-PROJ-B-PRV-PC-1 + type: computer + ip_address: 192.168.240.2 + default_gateway: 192.168.240.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-B-PRV-PC-2 + type: computer + ip_address: 192.168.240.3 + default_gateway: 192.168.240.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-B-PRV-PC-3 + type: computer + ip_address: 192.168.240.4 + default_gateway: 192.168.240.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT C # + ####################### + + - hostname: ST-PROJ-C-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-PROJ-C-PRV-PC-1 + type: computer + ip_address: 192.168.250.2 + default_gateway: 192.168.250.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-C-PRV-PC-2 + type: computer + ip_address: 192.168.250.3 + default_gateway: 192.168.250.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-C-PRV-PC-3 + type: computer + ip_address: 192.168.250.4 + default_gateway: 192.168.250.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + + ############################## + # Simulation Network Links # + ############################## + links: + + ############################ + # HOME OFFICE SUBNET LINKS # + ############################ + + # Home Switch (Port 1) --> Home Router (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 1 + + # Home Switch (Port 2) --> PC 1 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: HOME-PUB-PC-1 + endpoint_b_port: 1 + + # Home Switch (Port 3) --> PC 2 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: HOME-PUB-PC-2 + endpoint_b_port: 1 + + # Home Switch (Port 4) --> PC SRV (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: HOME-PUB-SRV + endpoint_b_port: 1 + + ################## + # Internet Links # + ################## + + # Internet Router (Port 1) --> Home Router (Port 2) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 2 + + # Internet Router (Port 2) --> DNS Server (Port 1) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 2 + endpoint_b_hostname: ISP-PUB-SRV-DNS + endpoint_b_port: 1 + + # Internet Router (Port 3) --> Remote Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-FW + endpoint_b_port: 1 + + #################### + # Remote DMZ Links # + #################### + + # Remote Firewall (Internal Port) --> Remote Site Router (Port 1) + - endpoint_a_hostname: REM-PUB-FW + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-RT-DR + endpoint_b_port: 1 + + #################### + # Remote Site Link # + #################### + + # Remote Site Router (Port 2) --> Remote Site Switch (Port 1) + - endpoint_a_hostname: REM-PUB-RT-DR + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-SW-AS + endpoint_b_port: 1 + + # Remote Site Switch (Port 2) --> Remote Site PC 1 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-PC-1 + endpoint_b_port: 1 + + # Remote Site Switch (Port 3) --> Remote Site PC 2 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-PC-2 + endpoint_b_port: 1 + + # Remote Site Switch (Port 4) --> Remote Site Server (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: REM-PUB-SRV + endpoint_b_port: 1 + + ####################### + # SOME_TECH DMZ Links # + ####################### + + # Internet Router (Port 4) --> Some Tech DMZ Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 4 + endpoint_b_hostname: ST-PUB-FW + endpoint_b_port: 1 + + # Some Tech DMZ Firewall (DMZ Port) --> Some Tech Web Server (Port 1) + - endpoint_a_hostname: ST-PUB-FW + endpoint_a_port: 3 + endpoint_b_hostname: ST-DMZ-PUB-SRV-WEB + endpoint_b_port: 1 + + ############################ + # SOME_TECH INTRANET Links # + ############################ + + # Some Tech Intranet CR Router (Port 1) --> Some Tech DMZ Firewall (Internal Port) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 1 + endpoint_b_hostname: ST-PUB-FW + endpoint_b_port: 2 + + # Some Tech Intranet CR Router (Port 2) --> Some Tech Intranet DR Router 1 (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 2 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_b_port: 1 + + # Some Tech Intranet CR Router (Port 3) --> Some Tech Intranet DR Router 2 (Port 2) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 3 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-2 + endpoint_b_port: 1 + + # Some Tech Intranet Private Router CR (Port 4) --> Some Tech Data Private Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 4 + endpoint_b_hostname: ST-DATA-PRV-SW-AS + endpoint_b_port: 1 + + + ############################### + # SOME_TECH HEAD OFFICE Links # + ############################### + + # Some Tech Head Office Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 2) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-2 + endpoint_b_port: 2 + + # Some Tech Head Office Switch (Port 2) --> Some Tech Head Office PC 1 (Port 1) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-HO-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 3) --> Some Tech Head Office PC 2 (Port 1) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-HO-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 4) --> Some Tech Head Office PC 3 (Port 1) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-HO-PRV-PC-3 + endpoint_b_port: 1 + + + ################################### + # SOME_TECH HUMAN RESOURCES Links # + ################################### + + # Some Tech Human Resources Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 3) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-2 + endpoint_b_port: 3 + + # Some Tech Human Resources Switch (Port 2) --> Some Tech Human Resources PC 1 (Port 1) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-HR-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 3) --> Some Tech Human Resources PC 2 (Port 1) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-HR-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 4) --> Some Tech Human Resources PC 3 (Port 1) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-HR-PRV-PC-3 + endpoint_b_port: 1 + + ######################## + # SOME_TECH DATA Links # + ######################## + + # Some Tech Data Switch (Port 2) --> Some Tech Data Private Storage Server (Port 1) + - endpoint_a_hostname: ST-DATA-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-DATA-PRV-SRV-STORAGE + endpoint_b_port: 1 + + # Some Tech Data Switch (Port 3) --> Some Tech Data Private Database Server (Port 1) + + - endpoint_a_hostname: ST-DATA-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-DATA-PRV-SRV-DB + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT A Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 2) --> Some Tech Private Project A Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 2) --> Some Tech Project A PC 1 + - endpoint_a_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-A-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 3) --> Some Tech Project A PC 2 + - endpoint_a_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-A-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 4) --> Some Tech Project A PC 3 + - endpoint_a_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-A-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT B Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 3) --> Some Tech Private Project B Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 2) --> Some Tech Project B PC 1 + - endpoint_a_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-B-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 3) --> Some Tech Project B PC 2 + - endpoint_a_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-B-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 4) --> Some Tech Project B PC 3 + - endpoint_a_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-B-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT C Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 4) --> Some Tech Private Project C Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 2) --> Some Tech Project C PC 1 + - endpoint_a_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-C-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 3) --> Some Tech Project C PC 2 + - endpoint_a_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-C-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 4) --> Some Tech Project C PC 3 + - endpoint_a_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-C-PRV-PC-3 + endpoint_b_port: 1 + +################################## +# Use Case 7 Agent YAML Anchors # +################################## + +############################## +# Green Agent YAML Anchors # +############################## + +# Green Agent Reward Impacts Values # + +LOW_WEIGHT_IMPACT: &LOW_WEIGHT_IMPACT 0.2 +MEDIUM_WEIGHT_IMPACT: &MEDIUM_WEIGHT_IMPACT 0.5 +HIGH_WEIGHT_IMPACT: &HIGH_WEIGHT_IMPACT 0.95 + +LOW_WEIGHT_IMPACT_NEGATIVE: &LOW_WEIGHT_IMPACT_NEG -0.2 +MEDIUM_WEIGHT_IMPACT_NEGATIVE: &MEDIUM_WEIGHT_IMPACT_NEG -0.5 +HIGH_WEIGHT_IMPACT_NEGATIVE: &HIGH_WEIGHT_IMPACT_NEG -0.8 + +# Default Green Agent Action Space Configuration Anchor # + +DEFAULT_GREEN_AGENT_MAX_EXECUTIONS: &DEFAULT_GREEN_AGENT_MAX_EXECUTIONS 1000 # Ensures green agent activity through-out an episode + +################################################# +# Probabilistic Green Agent Config Yaml Anchors # +################################################# + +# Probabilistic Green Agent | 20% NODE_APPLICATION_EXECUTE | 80% do-nothing # +PROBABILISTIC_CONFIG_20_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_20 + action_probabilities: + 0: 0.8 + 1: 0.2 + +# Probabilistic Green Agent | 40% NODE_APPLICATION_EXECUTE | 60% do-nothing # +PROBABILISTIC_CONFIG_40_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_40 + action_probabilities: + 0: 0.6 + 1: 0.4 + +# Probabilistic Green Agent | 60% NODE_APPLICATION_EXECUTE | 40% do-nothing # +PROBABILISTIC_CONFIG_20_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_60 + action_probabilities: + 0: 0.4 + 1: 0.6 + + +# System Green Agent Config UC7 Network Wide Yaml Anchor # + +# Lists the IP_Address of all hosts that contain DNS and NTP Service Clients +UC7_IP_LIST: &UC7_IP_LIST + # ====== Home Office ======== + - 192.168.1.2 # HOME-PUB-PC-1 | ip_id: 2 + - 192.168.1.3 # HOME-PUB-PC-2 | ip_id: 3 + - 192.168.1.4 # HOME-PUB-PC-SRV | ip_id: 4 + # ====== Remote Site ======== + - 192.168.20.2 # REM-PUB-PC-1 | ip_id: 5 + - 192.168.20.3 # REM-PUB-PC-2 | ip_id: 6 + - 192.168.20.4 # REM-PUB-SRV | ip_id: 7 + # ====== ST Public DMZ ======= + - *ST_PUB_SRV_WEB_IP # 192.168.100.2 (ST-DMZ-PUB-SRV-WEB) | ip_id: 8 + # ====== ST Head Office ======= + - 192.168.200.2 # ST-HO-PRV-PC-1 | ip_id: 9 + - 192.168.200.3 # ST-HO-PRV-PC-2 | ip_id: 10 + - 192.168.200.4 # ST-HO-PRV-PC-3 | ip_id: 11 + # ===== ST Human Resources ====== + - 192.168.210.2 # ST-HR-PRV-PC-1 | ip_id: 12 + - 192.168.210.3 # ST-HR-PRV-PC-2 | ip_id: 13 + - 192.168.210.4 # ST-HR-PRV-PC-3 | ip_id: 14 + # ====== ST DATA Servers ======= + - *ST_SRV_STORAGE_IP # 192.168.220.2 (ST-DATA-PRV-SRV-STORAGE) | ip_id: 15 + - *ST_SRV_DB_IP # 192.168.220.3 (ST-DATA-PRV-SRV-DB) | ip_id: 16 + # ====== ST Project A ======= + - 192.168.230.2 # PROJ-A-PRV-PC-1 | ip_id: 17 + - 192.168.230.3 # PROJ-A-PRV-PC-2 | ip_id: 18 + - 192.168.230.4 # PROJ-A-PRV-PC-3 | ip_id: 19 + # ====== ST Project B ======= + - 192.168.240.2 # PROJ-B-PRV-PC-1 | ip_id: 20 + - 192.168.240.3 # PROJ-B-PRV-PC-2 | ip_id: 21 + - 192.168.240.4 # PROJ-B-PRV-PC-3 | ip_id: 22 + # ====== ST Project C ======= + - 192.168.250.2 # PROJ-C-PRV-PC-1 | ip_id: 23 + - 192.168.250.3 # PROJ-C-PRV-PC-2 | ip_id: 24 + - 192.168.250.4 # PROJ-C-PRV-PC-3 | ip_id: 25 + +############################################ +# Use Case 7 Agent Configuration Section # +############################################ + +agents: + ####################################################### + # UC7 Green Agents Path of Life (POL) Configuration # + ####################################################### + + + #################################### + # Home Office Network POL Config # + #################################### + + # Home Office Green Agent Pattern Of Life + # ====================================== + # 1. Three Home workers accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. Three Home workers accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: HOME_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-1"] + target_application: "database-client" + start_step: 4 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-2"] + target_application: "database-client" + start_step: 8 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + - ref: HOME_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + #################################### + # Remote Site Network POL Config # + #################################### + + # Remote Site Green Agent Pattern Of Life + # ====================================== + # 1. Three Remote workers accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. Three Remote workers accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: REMOTE_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-1"] + target_application: "database-client" + start_step: 12 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-2"] + target_application: "database-client" + start_step: 16 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + - ref: REMOTE_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + #################################### + # ST Project A Network POL Config # + #################################### + + # ST Project A Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + + - ref: PROJ_A-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-A-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-1 + + - ref: PROJ_A-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-1 + + - ref: PROJ_A-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-A-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-A-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-A-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-3 + + - ref: PROJ_A-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-A-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-3 + + #################################### + # ST Project B Network POL Config # + #################################### + + # ST Project B Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: PROJ_B-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-B-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-1 + + - ref: PROJ_B-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-B-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-1 + + - ref: PROJ_B-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-B-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-B-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-3 + + - ref: PROJ_B-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-B-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-3 + + #################################### + # ST Project C Network POL Config # + #################################### + + # ST Project C Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: PROJ_C-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-C-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-1 + + - ref: PROJ_C-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-C-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-1 + + - ref: PROJ_C-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-C-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-C-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-C-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-3 + + - ref: PROJ_C-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-3 + + ###################################### + # ST Head Office Network POL Config # + ###################################### + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. The ST CEO accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 2. The ST CTO accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. The ST CFO accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: CEO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HO-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST-HO-PRV-PC-1 + + - ref: CTO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HO-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HO-PRV-PC-2 + + - ref: CFO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HO-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HO-PRV-PC-3 + + ########################################## + # ST Human Resources Network POL Config # + ########################################## + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. A senior HR staff accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 2. Two junior HR staff accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: SENIOR_HR + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HR-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HR-PRV-PC-1 + + - ref: JUNIOR_HR-1 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HR-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HR-PRV-PC-2 + + - ref: JUNIOR_HR-2 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HR-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HR-PRV-PC-3 + + ########################## + # UC7 Red Agent Config # + ########################## + + - ref: attacker + team: RED + type: tap-001 + agent_settings: + start_step: 1 + frequency: 5 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_target_ip: *ST_SRV_DB_IP + default_starting_node: "ST-PROJ-A-PRV-PC-1" + # starting_nodes: ["ST-PROJ-A-PRV-PC-1", "ST-PROJ-B-PRV-PC-2", "ST-PROJ-C-PRV-PC-3"] + starting_nodes: + kill_chain: + ACTIVATE: + probability: 1 + PROPAGATE: + probability: 1 + scan_attempts: 20 + repeat_scan: false + network_addresses: + - 192.168.230.0/29 # ST Project A + - 192.168.10.0/26 # Remote Site + - 192.168.20.0/30 # Remote DMZ + # - 192.168.240.0/29 # ST Project B + # - 192.168.250.0/29 # ST Project C + - 192.168.220.0/29 # ST Data (Contains Target) + COMMAND_AND_CONTROL: + probability: 1 + keep_alive_frequency: 5 + masquerade_port: HTTP + masquerade_protocol: TCP + c2_server_name: ISP-PUB-SRV-DNS + c2_server_ip: *PUBLIC_DNS_IP + PAYLOAD: + probability: 1 + exfiltrate: true + corrupt: true + exfiltration_folder_name: + target_username: admin + target_password: admin + continue_on_failed_exfil: True + + +########################### +# UC7 Blue Agent Config # +########################### + + - ref: defender + team: BLUE + type: proxy-agent + observation_space: + type: custom + options: + components: + - type: nodes + label: NODES + options: + hosts: + # TAP001 Potential Starting Note | ST-PROJ-A-PRV-PC-1 + - hostname: ST-PROJ-A-PRV-PC-1 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST-PROJ-B-PRV-PC-2 + - hostname: ST-PROJ-B-PRV-PC-2 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST-PROJ-C-PRV-PC-3 + - hostname: ST-PROJ-C-PRV-PC-3 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # ST DATA Server Database + - hostname: ST-DATA-PRV-SRV-DB + folders: + - folder_name: database + files: + - file_name: database.db + routers: + - hostname: ST-INTRA-PRV-RT-CR # TAP003 Malicious ACL Target | ROUTER0 + - hostname: ST-INTRA-PRV-RT-DR-1 # TAP003 Malicious ACL Target | ROUTER1 + - hostname: REM-PUB-RT-DR # TAP003 Malicious ACL Target | ROUTER2 + num_ports: 5 + num_services: 2 + num_applications: 2 + num_folders: 2 + num_files: 1 + num_nics: 1 + ip_list: + # ip_list is indexed at 2: + # 0 reserved for padding to align with observations + # 1 reserved for ALL ips + *UC7_IP_LIST + wildcard_list: + - 0.0.0.1 # 0 + - 0.0.0.255 # 1 + - 0.0.255.255 # 2 + port_list: + # 0 is a padding placeholder + # 1 means ALL ports + - FTP # 2 + - DNS # 3 + - HTTP # 4 + - NTP # 5 + - POSTGRES_SERVER # 6 + - SSH # 7 + + protocol_list: + # 0 is padding placeholder + # 1 means ALL protocols + - ICMP # 2 + - TCP # 3 + - UDP # 4 + num_rules: 5 + include_num_access: true + include_nmne: true + monitored_traffic: + tcp: + - HTTP + - POSTGRES_SERVER + icmp: + - NONE + - type: links + label: LINKS + options: + link_references: + # HOME OFFICE SUBNET LINKS + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-RT-DR:eth-1 # 1 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-1:eth-1 # 2 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-2:eth-1 # 3 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-SRV:eth-1 # 4 + # Internet LINKS + - ISP-PUB-RT-BR:eth-1<->HOME-PUB-RT-DR:eth-2 # 5 + - ISP-PUB-RT-BR:eth-2<->ISP-PUB-SRV-DNS:eth-1 # 6 + - ISP-PUB-RT-BR:eth-3<->REM-PUB-FW:eth-1 # 7 + # Remote DMZ Links + - REM-PUB-FW:eth-2<->REM-PUB-RT-DR:eth-1 # 8 + # Remote Site Links + - REM-PUB-RT-DR:eth-2<->REM-PUB-SW-AS:eth-1 # 9 + - REM-PUB-SW-AS:eth-2<->REM-PUB-PC-1:eth-1 # 10 + - REM-PUB-SW-AS:eth-3<->REM-PUB-PC-2:eth-1 # 11 + - REM-PUB-SW-AS:eth-4<->REM-PUB-SRV:eth-1 # 12 + # SOME_TECH DMZ + - ISP-PUB-RT-BR:eth-4<->ST-PUB-FW:eth-1 # 13 + - ST-PUB-FW:eth-3<->ST-DMZ-PUB-SRV-WEB:eth-1 # 14 + # SOME_TECH Intranet + - ST-INTRA-PRV-RT-CR:eth-1<->ST-PUB-FW:eth-2 # 15 + - ST-INTRA-PRV-RT-CR:eth-2<->ST-INTRA-PRV-RT-DR-1:eth-1 # 16 + - ST-INTRA-PRV-RT-CR:eth-3<->ST-INTRA-PRV-RT-DR-2:eth-1 # 17 + - ST-INTRA-PRV-RT-CR:eth-4<->ST-DATA-PRV-SW-AS:eth-1 # 18 + # SOME_TECH Head Office + - ST-HO-PRV-SW-AS:eth-1<->ST-INTRA-PRV-RT-DR-2:eth-2 # 19 + - ST-HO-PRV-SW-AS:eth-2<->ST-HO-PRV-PC-1:eth-1 # 20 + - ST-HO-PRV-SW-AS:eth-3<->ST-HO-PRV-PC-2:eth-1 # 21 + - ST-HO-PRV-SW-AS:eth-4<->ST-HO-PRV-PC-3:eth-1 # 22 + # SOME_TECH Human Resources + - ST-HR-PRV-SW-AS:eth-1<->ST-INTRA-PRV-RT-DR-2:eth-3 # 23 + - ST-HR-PRV-SW-AS:eth-2<->ST-HR-PRV-PC-1:eth-1 # 24 + - ST-HR-PRV-SW-AS:eth-3<->ST-HR-PRV-PC-2:eth-1 # 25 + - ST-HR-PRV-SW-AS:eth-4<->ST-HR-PRV-PC-3:eth-1 # 26 + # SOME_TECH Data Links + - ST-DATA-PRV-SW-AS:eth-2<->ST-DATA-PRV-SRV-STORAGE:eth-1 # 27 + - ST-DATA-PRV-SW-AS:eth-3<->ST-DATA-PRV-SRV-DB:eth-1 # 28 + # SOME_TECH Project A Links + - ST-INTRA-PRV-RT-DR-1:eth-2<->ST-PROJ-A-PRV-SW-AS:eth-1 # 29 + - ST-PROJ-A-PRV-SW-AS:eth2<->ST-PROJ-A-PRV-PC-1:eth-1 # 31 + - ST-PROJ-A-PRV-SW-AS:eth3<->ST-PROJ-A-PRV-PC-2:eth-1 # 32 + - ST-PROJ-A-PRV-SW-AS:eth4<->ST-PROJ-A-PRV-PC-3:eth-1 # 33 + # SOME_TECH Project B Links + - ST-INTRA-PRV-RT-DR-1:eth-3<->ST-PROJ-B-PRV-SW-AS:eth-1 # 34 + - ST-PROJ-B-PRV-SW-AS:eth2<->ST-PROJ-B-PRV-PC-1:eth-1 # 35 + - ST-PROJ-B-PRV-SW-AS:eth3<->ST-PROJ-B-PRV-PC-2:eth-1 # 36 + - ST-PROJ-B-PRV-SW-AS:eth4<->ST-PROJ-B-PRV-PC-3:eth-1 # 37 + # SOME_TECH Project C Links + - ST-INTRA-PRV-RT-DR-1:eth-4<->ST-PROJ-C-PRV-SW-AS:eth-1 # 38 + - ST-PROJ-A-PRV-SW-AS:eth2<->ST-PROJ-C-PRV-PC-1:eth-1 # 39 + - ST-PROJ-A-PRV-SW-AS:eth3<->ST-PROJ-C-PRV-PC-2:eth-1 # 40 + - ST-PROJ-A-PRV-SW-AS:eth4<->ST-PROJ-C-PRV-PC-3:eth-1 # 41 + action_space: + action_map: + 0: + action: do-nothing + options: {} + + # |======================================| + # | ST-PROJ-A-PRV-PC-1 | + # |======================================| + + # ST-PROJ-A-PRV-PC-1 | node-os-scan + 1: + action: node-os-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | node-shutdown + 2: + action: node-shutdown + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | node-startup + 3: + action: node-startup + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | node-reset + 4: + action: node-reset + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | host-nic-disable + 5: + action: host-nic-disable + options: + node_name: ST-PROJ-A-PRV-PC-1 + nic_num: 0 + # ST-PROJ-A-PRV-PC-1 | host-nic-enable + 6: + action: host-nic-enable + options: + node_name: ST-PROJ-A-PRV-PC-1 + nic_num: 0 + # ST-PROJ-A-PRV-PC-1 | node-application-close | database-client + 7: + action: node-application-close + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + # ST-PROJ-A-PRV-PC-1 | node-application-scan | database-client + 8: + action: node-application-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + # ST-PROJ-A-PRV-PC-1 | node-application-fix | database-client + 9: + action: node-application-fix + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + # ST-PROJ-A-PRV-PC-1 | node-application-remove | database-client + 10: + action: node-application-remove + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + + # ST-PROJ-A-PRV-PC-1 | node-file-scan | downloads/malware_dropper.ps1 + 11: + action: node-file-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST-PROJ-A-PRV-PC-1 | node-file-scan | exfiltration_folder/database.db + 12: + action: node-file-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + file_name: database.db + + # ST-PROJ-A-PRV-PC-1 | node-folder-scan | downloads/ + 13: + action: node-folder-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: downloads + + # ST-PROJ-A-PRV-PC-1 | node-folder-scan | exfiltration_folder/ + 14: + action: node-folder-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + + # |======================================| + # | ST-PROJ-B-PRV-PC-2 | + # |======================================| + + # ST-PROJ-B-PRV-PC-2 | node-os-scan + 15: + action: node-os-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | node-shutdown + 16: + action: node-shutdown + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | node-startup + 17: + action: node-startup + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | node-reset + 18: + action: node-reset + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | host-nic-disable + 19: + action: host-nic-disable + options: + node_name: ST-PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST-PROJ-B-PRV-PC-2 | host-nic-enable + 20: + action: host-nic-enable + options: + node_name: ST-PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST-PROJ-B-PRV-PC-2 | node-application-close | database-client + 21: + action: node-application-close + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-application-scan | database-client + 22: + action: node-application-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-application-fix | database-client + 23: + action: node-application-fix + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-application-remove | database-client + 24: + action: node-application-remove + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-file-scan | downloads/malware_dropper.ps1 + 25: + action: node-file-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST-PROJ-B-PRV-PC-2 | node-file-scan | exfiltration_folder/database.db + 26: + action: node-file-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + file_name: database.db + + # ST-PROJ-B-PRV-PC-2 | node-folder-scan | downloads/ + 27: + action: node-folder-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: downloads + + # ST-PROJ-B-PRV-PC-2 | node-folder-scan | exfiltration_folder/ + 28: + action: node-folder-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + + # |======================================| + # | ST-PROJ-C-PRV-PC-3 | + # |======================================| + + # ST-PROJ-C-PRV-PC-3 | node-os-scan + 29: + action: node-os-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | node-shutdown + 30: + action: node-shutdown + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | node-startup + 31: + action: node-startup + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | node-reset + 32: + action: node-reset + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | host-nic-disable + 33: + action: host-nic-disable + options: + node_name: ST-PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST-PROJ-C-PRV-PC-3 | host-nic-enable + 34: + action: host-nic-enable + options: + node_name: ST-PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST-PROJ-C-PRV-PC-3 | node-application-close | database-client + 35: + action: node-application-close + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-application-scan | database-client + 36: + action: node-application-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-application-fix | database-client + 37: + action: node-application-fix + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-application-remove | database-client + 38: + action: node-application-remove + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-file-scan | downloads/malware_dropper.ps1 + 39: + action: node-file-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST-PROJ-C-PRV-PC-3 | node-file-scan | exfiltration_folder/database.db + 40: + action: node-file-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + file_name: database.db + + # ST-PROJ-C-PRV-PC-3 | node-folder-scan | downloads/ + 41: + action: node-folder-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: downloads + + # ST-PROJ-C-PRV-PC-3 | node-folder-scan | exfiltration_folder/ + 42: + action: node-folder-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + + # |======================================| + # | ST-INTRA-PRV-RT-CR | + # |======================================| + + # ST-INTRA-PRV-RT-CR | router-acl-add-rule | P2: ST-PROJ-A-PRV-PC-1 !==> ST-DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 43: + action: router-acl-add-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST-PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST-DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST-INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 1 + 44: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 1 + + # ST-INTRA-PRV-RT-CR | router-acl-add-rule | P3: ST-PROJ-B-PRV-PC-2 !==> ST-DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 45: + action: router-acl-add-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 2 + permission: DENY + src_ip: 192.168.240.3 # (ST-PROJ-B-PRV-PC-2) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST-DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + # ST-INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 2 + 46: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 2 + + # ST-INTRA-PRV-RT-CR | router-acl-add-rule | P4: ST-PROJ-C-PRV-PC-3 !==> ST-DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 47: + action: router-acl-add-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 3 + permission: DENY + src_ip: 192.168.250.4 # (ST-PROJ-C-PRV-PC-3) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST-DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST-INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 3 + 48: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 3 + + # |======================================| + # | ST-DATA-PRV-SRV-DB | + # |======================================| + + # ST-DATA-PRV-SRV-DB | node-file-scan | Scans the database.db file (health status) + 49: + action: node-file-scan + options: + node_name: ST-DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # ST-DATA-PRV-SRV-DB | node-account-change-password | Changes the password of a user account + 50: + action: node-account-change-password + options: + node_name: ST-DATA-PRV-SRV-DB + username: admin # default account + current_password: admin # default password + new_password: thr33_alert_wolv3z # A more 'secure' password + + # |======================================| + # | ST-INTRA-PRV-RT-DR-1 | + # |======================================| + + # ST-INTRA-PRV-RT-DR-1 | router-acl-add-rule | P1: ST-INTRA-PRV-RT-DR-1 !==> ANY (TCP:SSH) + 51: + action: router-acl-add-rule + options: + target_router: ST-INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST-PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: SSH + dst_ip: ALL + dst_wildcard: 0.0.255.255 + dst_port: SSH + protocol_name: TCP + + # ST-INTRA-PRV-RT-DR-1 | node-account-change-password + 52: + action: node-account-change-password + options: + node_name: ST-INTRA-PRV-RT-DR-1 + username: admin + current_password: admin + new_password: secure_password + + # ST-INTRA-PRV-RT-DR-1 | router-acl-remove-rule | Removes the given ACL at position 1 + 53: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-DR-1 + position: 1 + + # |======================================| + # | REM-PUB-RT-DR | + # |======================================| + + # REM-PUB-RT-DR | node-account-change-password + 54: + action: node-account-change-password + options: + node_name: REM-PUB-RT-DR + username: admin + current_password: admin + new_password: secure_password + + # REM-PUB-RT-DR | router-acl-remove-rule | Removes the given ACL at position 1 + 55: + action: router-acl-remove-rule + options: + target_router: REM-PUB-RT-DR + position: 1 + + reward_function: + reward_components: + - type: database-file-integrity + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST-DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # Home Site Green Agents (32 Green Agents each contributing 0.03125 of blue reward) + + # Blue Shared Reward | HOME_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-DB + + # Blue Shared Reward | HOME_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-WEB + + # Blue Shared Reward | HOME_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-DB + + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-WEB + + # Remote Site Green Agents + + # Blue Shared Reward | REMOTE_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-DB + + # Blue Shared Reward | REMOTE_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-WEB + + # Blue Shared Reward | REMOTE_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-DB + + # Blue Shared Reward | REMOTE_WORKER-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-WEB + + # ST Project A Green Agents + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-WEB + + # ST Project B Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Project C Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Head Office Green Agents (CEO/CFO/CTO) + + # Blue Shared Reward | CEO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CEO + + # Blue Shared Reward | CFO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CFO + + # Blue Shared Reward | CTO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CTO + + # ST Human Resources Green Agents + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: SENIOR_HR + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-1 + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-2 + + agent_settings: + flatten_obs: True + + + + # ####################################### + # # UC7 Network Wide System POL Config # + # ####################################### + + # # UC7 Network Wide System Pattern Of Life + # # ========================================== + # # 1. All NTP Clients perform periodic NTP Server transactions. + # # 2. Periodic Database (ST-DATA-PRV-SRV-DB) FTP backups (ST-DATA-PRV-SRV-STORAGE) + # # 3. All DNS Clients perform periodic DNS Server transactions. + + # # TODO: NTPClient Node Service Start doesn't start a 'Request' + + # # 1. All NTP Clients perform periodic NTP Server transactions. + + # - ref: SYSTEM-NTP + # team: GREEN + # type: probabilistic-agent + # observation_space: null + # action_space: + # action_list: + # - type: do-nothing + # - type: NODE_SERVICE_START + # action_map: + # # ====== Home Office ======== + # 0: + # action: do-nothing + # options: {} + # 1: + # action: NODE_SERVICE_START + # options: + # node_id: 0 # HOME-PUB-PC-1 + # service_id: 0 # NTPClient + # 2: + # action: NODE_SERVICE_START + # options: + # node_id: 1 # HOME-PUB-PC-2 + # service_id: 0 + # 3: + # action: NODE_SERVICE_START + # options: + # node_id: 2 # HOME-PUB-PC-SRV + # service_id: 0 + # # ====== Remote Site ======== + # 4: + # action: NODE_SERVICE_START + # options: + # node_id: 3 # REM-PUB-PC-1 + # service_id: 0 + # 5: + # action: NODE_SERVICE_START + # options: + # node_id: 4 # REM-PUB-PC-2 + # service_id: 0 + # 6: + # action: NODE_SERVICE_START + # options: + # node_id: 5 # REM-PUB-SRV + # service_id: 0 + # # ====== ST Public DMZ ======= + # 7: + # action: NODE_SERVICE_START + # options: + # node_id: 6 # ST-DMZ-PUB-SRV-WEB + # service_id: 0 + # # ====== ST Head Office ======= + # 8: + # action: NODE_SERVICE_START + # options: + # node_id: 7 # ST-HO-PRV-PC-1 + # service_id: 0 + # 9: + # action: NODE_SERVICE_START + # options: + # node_id: 8 # ST-HO-PRV-PC-2 + # service_id: 0 + # 10: + # action: NODE_SERVICE_START + # options: + # node_id: 9 # ST-HO-PRV-PC-3 + # service_id: 0 + # # ====== ST Human Resources ======= + # 11: + # action: NODE_SERVICE_START + # options: + # node_id: 10 # ST-HR-PRV-PC-1 + # service_id: 0 + # 12: + # action: NODE_SERVICE_START + # options: + # node_id: 11 # ST-HR-PRV-PC-2 + # service_id: 0 + # 13: + # action: NODE_SERVICE_START + # options: + # node_id: 12 # ST-HR-PRV-PC-3 + # service_id: 0 + # # ====== ST DATA Servers ======= + # 14: + # action: NODE_SERVICE_START + # options: + # node_id: 13 # ST-DATA-PRV-SRV-STORAGE + # service_id: 0 + # 15: + # action: NODE_SERVICE_START + # options: + # node_id: 14 # ST-DATA-PRV-SRV-DB + # service_id: 0 + # # ====== ST Project A ======= + # 16: + # action: NODE_SERVICE_START + # options: + # node_id: 15 # PROJ-A-PRV-PC-1 + # service_id: 0 + # 17: + # action: NODE_SERVICE_START + # options: + # node_id: 16 # PROJ-A-PRV-PC-2 + # service_id: 0 + # 18: + # action: NODE_SERVICE_START + # options: + # node_id: 17 # PROJ-A-PRV-PC-3 + # service_id: 0 + # # ====== ST Project B ======= + # 19: + # action: NODE_SERVICE_START + # options: + # node_id: 18 # PROJ-B-PRV-PC-1 + # service_id: 0 + # 20: + # action: NODE_SERVICE_START + # options: + # node_id: 19 # PROJ-B-PRV-PC-2 + # service_id: 0 + # 21: + # action: NODE_SERVICE_START + # options: + # node_id: 20 # PROJ-B-PRV-PC-3 + # service_id: 0 + # # ====== ST Project C ======= + # 22: + # action: NODE_SERVICE_START + # options: + # node_id: 21 # PROJ-C-PRV-PC-1 + # service_id: 0 + # 23: + # action: NODE_SERVICE_START + # options: + # node_id: 22 # PROJ-C-PRV-PC-2 + # service_id: 0 + # 24: + # action: NODE_SERVICE_START + # options: + # node_id: 23 # PROJ-C-PRV-PC-3 + # service_id: 0 + # options: + # nodes: + # # ====== Home Office ======== + # - node_name: HOME-PUB-PC-1 + # services: + # - service_name: NTPClient + # - node_name: HOME-PUB-PC-2 + # services: + # - service_name: NTPClient + # - node_name: HOME-PUB-SRV + # services: + # - service_name: NTPClient + # # ====== Remote Site ======== + # - node_name: REM-PUB-PC-1 + # services: + # - service_name: NTPClient + # - node_name: REM-PUB-PC-2 + # services: + # - service_name: NTPClient + # - node_name: REM-PUB-SRV + # services: + # - service_name: NTPClient + # # ====== ST Public DMZ ======= + # - node_name: ST-DMZ-PUB-SRV-WEB + # services: + # - service_name: NTPClient + # # ====== ST Head Office ======= + # - node_name: ST-HO-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: ST-HO-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: ST-HO-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ===== ST Human Resources ====== + # - node_name: ST-HR-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: ST-HR-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: ST-HR-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ====== ST DATA Servers ======= + # - node_name: ST-DATA-PRV-SRV-STORAGE + # services: + # - service_name: NTPClient + # - node_name: ST-DATA-PRV-SRV-DB + # services: + # - service_name: NTPClient + # # ====== ST Project A ======= + # - node_name: PROJ-A-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: PROJ-A-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: PROJ-A-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ====== ST Project B ======= + # - node_name: PROJ-B-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: PROJ-B-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: PROJ-B-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ====== ST Project C ======= + # - node_name: PROJ-C-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: PROJ-C-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: PROJ-C-PRV-PC-3 + # services: + # - service_name: NTPClient + # ip_list: + # *UC7_IP_LIST + # max_folders_per_node: 1 + # max_files_per_folder: 1 + # max_services_per_node: 1 + # max_applications_per_node: 1 + # settings: + # start_step: 1 + # start_variance: 0 + # max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + # frequency: 1 + # variance: 0 + # reward_function: + # reward_components: + # - type: DUMMY + + # # 2. Periodic Database (ST-DATA-PRV-SRV-DB) FTP backups (ST-DATA-PRV-SRV-STORAGE) + + # - ref: SYSTEM-FTP + # team: GREEN + # type: probabilistic-agent + # observation_space: null + # action_space: + # action_list: + # - type: do-nothing + # - type: NODE_SERVICE_FIX + # action_map: + # 0: + # action: do-nothing + # options: {} + # 1: + # action: NODE_SERVICE_FIX + # options: + # node_id: 0 + # service_id: 0 + # options: + # nodes: + # - node_name: ST-DATA-PRV-SRV-DB + # applications: + # - application_name: database-client + # ip_list: + # - *ST_SRV_DB_IP + # max_folders_per_node: 1 + # max_files_per_folder: 1 + # max_services_per_node: 1 + # max_applications_per_node: 1 + # settings: + # start_step: 4 + # start_variance: 0 + # max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + # frequency: 4 + # variance: 0 + # reward_function: + # reward_components: + # - type: DUMMY + + # # 3. All DNS Clients perform periodic DNS Server transactions. + + # # TODO: Address issue where dns-clients can't be started directly via agents + # - ref: SYSTEM-DNS + # team: GREEN + # type: probabilistic-agent + # observation_space: null + # action_space: + # action_list: + # - type: do-nothing + # - type: NODE_SERVICE_START + # action_map: + # # ====== Home Office ======== + # 0: + # action: do-nothing + # options: {} + # 1: + # action: NODE_SERVICE_START + # options: + # node_id: 0 # HOME-PUB-PC-1 + # service_id: 0 # dns-client + # 2: + # action: NODE_SERVICE_START + # options: + # node_id: 1 # HOME-PUB-PC-2 + # service_id: 0 + # 3: + # action: NODE_SERVICE_START + # options: + # node_id: 2 # HOME-PUB-PC-SRV + # service_id: 0 + # # ====== Remote Site ======== + # 4: + # action: NODE_SERVICE_START + # options: + # node_id: 3 # REM-PUB-PC-1 + # service_id: 0 + # 5: + # action: NODE_SERVICE_START + # options: + # node_id: 4 # REM-PUB-PC-2 + # service_id: 0 + # 6: + # action: NODE_SERVICE_START + # options: + # node_id: 5 # REM-PUB-SRV + # service_id: 0 + # # ====== ST Public DMZ ======= + # 7: + # action: NODE_SERVICE_START + # options: + # node_id: 6 # ST-DMZ-PUB-SRV-WEB + # service_id: 0 + # # ====== ST Head Office ======= + # 8: + # action: NODE_SERVICE_START + # options: + # node_id: 7 # ST-HO-PRV-PC-1 + # service_id: 0 + # 9: + # action: NODE_SERVICE_START + # options: + # node_id: 8 # ST-HO-PRV-PC-2 + # service_id: 0 + # 10: + # action: NODE_SERVICE_START + # options: + # node_id: 9 # ST-HO-PRV-PC-3 + # service_id: 0 + # # ====== ST Human Resources ======= + # 11: + # action: NODE_SERVICE_START + # options: + # node_id: 10 # ST-HR-PRV-PC-1 + # service_id: 0 + # 12: + # action: NODE_SERVICE_START + # options: + # node_id: 11 # ST-HR-PRV-PC-2 + # service_id: 0 + # 13: + # action: NODE_SERVICE_START + # options: + # node_id: 12 # ST-HR-PRV-PC-3 + # service_id: 0 + # # ====== ST DATA Servers ======= + # 14: + # action: NODE_SERVICE_START + # options: + # node_id: 13 # ST-DATA-PRV-SRV-STORAGE + # service_id: 0 + # 15: + # action: NODE_SERVICE_START + # options: + # node_id: 14 # ST-DATA-PRV-SRV-DB + # service_id: 0 + # # ====== ST Project A ======= + # 16: + # action: NODE_SERVICE_START + # options: + # node_id: 15 # PROJ-A-PRV-PC-1 + # service_id: 0 + # 17: + # action: NODE_SERVICE_START + # options: + # node_id: 16 # PROJ-A-PRV-PC-2 + # service_id: 0 + # 18: + # action: NODE_SERVICE_START + # options: + # node_id: 17 # PROJ-A-PRV-PC-3 + # service_id: 0 + # # ====== ST Project B ======= + # 19: + # action: NODE_SERVICE_START + # options: + # node_id: 18 # PROJ-B-PRV-PC-1 + # service_id: 0 + # 20: + # action: NODE_SERVICE_START + # options: + # node_id: 19 # PROJ-B-PRV-PC-2 + # service_id: 0 + # 21: + # action: NODE_SERVICE_START + # options: + # node_id: 20 # PROJ-B-PRV-PC-3 + # service_id: 0 + # # ====== ST Project C ======= + # 22: + # action: NODE_SERVICE_START + # options: + # node_id: 21 # PROJ-C-PRV-PC-1 + # service_id: 0 + # 23: + # action: NODE_SERVICE_START + # options: + # node_id: 22 # PROJ-C-PRV-PC-2 + # service_id: 0 + # 24: + # action: NODE_SERVICE_START + # options: + # node_id: 23 # PROJ-C-PRV-PC-3 + # service_id: 0 + # options: + # nodes: + # # ====== Home Office ======== + # - node_name: HOME-PUB-PC-1 + # services: + # - service_name: dns-client + # - node_name: HOME-PUB-PC-2 + # services: + # - service_name: dns-client + # - node_name: HOME-PUB-SRV + # services: + # - service_name: dns-client + # # ====== Remote Site ======== + # - node_name: REM-PUB-PC-1 + # services: + # - service_name: dns-client + # - node_name: REM-PUB-PC-2 + # services: + # - service_name: dns-client + # - node_name: REM-PUB-SRV + # services: + # - service_name: dns-client + # # ====== ST Public DMZ ======= + # - node_name: ST-DMZ-PUB-SRV-WEB + # services: + # - service_name: dns-client + # # ====== ST Head Office ======= + # - node_name: ST-HO-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: ST-HO-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: ST-HO-PRV-PC-3 + # services: + # - service_name: dns-client + # # ===== ST Human Resources ====== + # - node_name: ST-HR-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: ST-HR-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: ST-HR-PRV-PC-3 + # services: + # - service_name: dns-client + # # ====== ST DATA Servers ======= + # - node_name: ST-DATA-PRV-SRV-STORAGE + # services: + # - service_name: dns-client + # - node_name: ST-DATA-PRV-SRV-DB + # services: + # - service_name: dns-client + # # ====== ST Project A ======= + # - node_name: PROJ-A-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: PROJ-A-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: PROJ-A-PRV-PC-3 + # services: + # - service_name: dns-client + # # ====== ST Project B ======= + # - node_name: PROJ-B-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: PROJ-B-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: PROJ-B-PRV-PC-3 + # services: + # - service_name: dns-client + # # ====== ST Project C ======= + # - node_name: PROJ-C-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: PROJ-C-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: PROJ-C-PRV-PC-3 + # services: + # - service_name: dns-client + # ip_list: + # *UC7_IP_LIST + # max_folders_per_node: 1 + # max_files_per_folder: 1 + # max_services_per_node: 1 + # max_applications_per_node: 1 + # settings: + # start_step: 1 + # start_variance: 0 + # max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + # frequency: 1 + # variance: 0 + # reward_function: + # reward_components: + # - type: DUMMY diff --git a/src/primaite/config/_package_data/uc7_config_tap003.yaml b/src/primaite/config/_package_data/uc7_config_tap003.yaml new file mode 100644 index 00000000..f2d86236 --- /dev/null +++ b/src/primaite/config/_package_data/uc7_config_tap003.yaml @@ -0,0 +1,3303 @@ +########################################################## +# USE CASE 7 CONFIGURATION YAML FILE # +########################################################## + + +########################################## +# PrimAITE Game and Logging Settings # +########################################## + + +# PrimAITE I/O Settings # + +io_settings: + save_agent_actions: false + save_step_metadata: false + save_pcap_logs: false + save_sys_logs: True + # save_sys_logs: true + write_sys_log_to_terminal: false + + +# PrimAITE Game Settings # + +game: + max_episode_length: 128 + ports: + - FTP + - DNS + - HTTP + - NTP + - POSTGRES_SERVER + - SSH + protocols: + - ICMP + - TCP + - UDP + thresholds: + nmne: + high: 10 + medium: 5 + low: 0 + +############################################ +# PrimAITE Use Case 7 Simulation # +############################################ + +########################################## +# Configuration Variables (Yaml Anchors) # +########################################## + +# External Network Address List # + +DNS_SUBNET: &DNS_SUBNET 255.255.255.240 # | 8.8.8.0 / 28 +HOME_INTERNET_SUBNET: &HOME_INTERNET_SUBNET 255.255.255.252 # | 10.1.0.0 / 30 +REMOTE_INTERNET_SUBNET: &REMOTE_INTERNET_SUBNET 255.255.255.252 # | 10.1.10.0 / 30 +SOME_TECH_INTERNET_SUBNET: &ST_INTERNET_SUBNET 255.255.255.252 # | 10.1.100.0 / 30 +HOME_OFFICE_SUBNET: &HOME_SUBNET 255.255.255.0 # | 192.168.1.0 / 26 +REMOTE_SUBNET_DMZ: &REMOTE_SUBNET_DMZ 255.255.255.252 # | 192.168.10.0 / 30 +REMOTE_SUBNET: &REMOTE_SUBNET 255.255.255.240 # | 192.168.20.0 / 28 + +# SOME_TECH (ST) Network Address List # + +SOME_TECH_DMZ_SUBNET: &ST_DMZ_SUBNET 255.255.255.252 # | 192.168.100.0 / 30 +SOME_TECH_INTRANET_RT_CR_SUBNET: &ST_INTRA_CR_SUBNET 255.255.255.240 # | 192.168.150.0 / 28 +SOME_TECH_INTRANET_RT_DR_ONE_SUBNET: &ST_INTRA_DR_ONE_SUBNET 255.255.255.252 # | 192.168.160.0 / 30 +SOME_TECH_INTRANET_RT_DR_TWO_SUBNET: &ST_INTRA_DR_TWO_SUBNET 255.255.255.252 # | 192.168.170.0 / 30 +SOME_TECH_HEAD_OFFICE_SUBNET: &ST_HO_SUBNET 255.255.255.248 # | 192.168.200.0 / 29 +SOME_TECH_HUMAN_RESOURCES_SUBNET: &ST_HR_SUBNET 255.255.255.248 # | 192.168.210.0 / 29 +SOME_TECH_DATA_SUBNET: &ST_DATA_SUBNET 255.255.255.248 # | 192.168.220.0 / 29 +SOME_TECH_PROJECT_A_SUBNET: &ST_PROJ_A_SUBNET 255.255.255.248 # | 192.168.230.0 / 29 +SOME_TECH_PROJECT_B_SUBNET: &ST_PROJ_B_SUBNET 255.255.255.248 # | 192.168.240.0 / 29 +SOME_TECH_PROJECT_C_SUBNET: &ST_PROJ_C_SUBNET 255.255.255.248 # | 192.168.250.0 / 29 + +# Host & Server Configurations # + +# ST Public Web Server | web-server | ST-DMZ-PUB-SRV-WEB +SOME_TECH_PUBLIC_SERVER_WEB_IP_ADDRESS: &ST_PUB_SRV_WEB_IP 192.168.100.2 +SOME_TECH_PUBLIC_SERVER_WEB_CONFIG: &ST_SRV_WEB_CONFIG # + - type: web-server + +# ISP Public DNS | dns-server | ISP-PUB-SRV-DNS +PUBLIC_DNS_IP_ADDRESS: &PUBLIC_DNS_IP 8.8.8.8 +PUBLIC_DNS_CONFIG: &PUBLIC_DNS_CONFIG # + - type: dns-server + options: + domain_mapping: + some_tech.com: *ST_PUB_SRV_WEB_IP + +# ST Private Storage Server | ftp-server | ST-DATA-PRV-SRV-STORAGE +SOME_TECH_PRIVATE_SERVER_STORAGE_IP: &ST_SRV_STORAGE_IP 192.168.220.2 +SOME_TECH_PRIVATE_SERVER_STORAGE_CONFIG: &ST_SRV_STORAGE_CONFIG + - type: ftp-server + +# ST Private Database Server | database-client & ftp-client | ST-DATA-PRV-SRV-DB +SOME_TECH_PRIVATE_SERVER_DATABASE_IP: &ST_SRV_DB_IP 192.168.220.3 +SOME_TECH_PRIVATE_SERVER_DATABASE_CONFIG: &ST_SRV_DB_CONFIG + - type: database-service + options: + backup_server_ip: *ST_SRV_STORAGE_IP + - type: ftp-client + +# Default PC Configuration | Database Client & Web Server +PERSONAL_COMPUTER_DEFAULT_CONFIG: &PC_DEFAULT_CONFIG + - type: database-client + options: + db_server_ip: *ST_SRV_DB_IP + - type: web-browser + options: + target_url: http://some_tech.com + + +############################## +# Simulation Configuration # +############################## + +simulation: + defaults: + folder_scan_duration: 0 + folder_restore_duration: 3 + service_fix_duration: 2 + service_restart_duration: 2 + software_install_duration: 0 + node_start_up_duration: 3 + node_shut_down_duration: 3 + node_scan_duration: 8 + network: + nmne_config: + capture_nmne: true + nmne_capture_keywords: + - DELETE + - ENCRYPT + nodes: + ###################### + # HOME OFFICE SUBNET # + ###################### + - hostname: HOME-PUB-RT-DR + type: router + default_gateway: 10.1.0.1 + ports: + 1: + ip_address: 192.168.1.1 + subnet_mask: *HOME_SUBNET + 2: + ip_address: 10.1.0.2 + subnet_mask: *HOME_INTERNET_SUBNET + default_route: + next_hop_ip_address: 10.1.0.1 + acl: + 5: + action: PERMIT + + - hostname: HOME-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: HOME-PUB-PC-1 + type: computer + ip_address: 192.168.1.2 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-PC-2 + type: computer + ip_address: 192.168.1.3 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-SRV + type: server + ip_address: 192.168.1.4 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + + ###################### + # INTERNET SUBNET # + ###################### + - hostname: ISP-PUB-RT-BR + type: router + ports: + 1: + ip_address: 10.1.0.1 + subnet_mask: *HOME_INTERNET_SUBNET + 2: + ip_address: 8.8.8.1 + subnet_mask: *DNS_SUBNET + 3: + ip_address: 10.1.10.1 + subnet_mask: *REMOTE_INTERNET_SUBNET + 4: + ip_address: 10.1.100.1 + subnet_mask: *ST_INTERNET_SUBNET + routes: + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.0.2 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 8.8.8.8 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.10.2 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.10.2 + + default_route: + next_hop_ip_address: 10.1.100.2 # SOME_TECH Firewall + + acl: + 5: + action: PERMIT + + ################ + # DNS SUBNET # + ################ + - hostname: ISP-PUB-SRV-DNS + type: server + ip_address: 8.8.8.8 + subnet_mask: *DNS_SUBNET + default_gateway: 8.8.8.1 + services: + *PUBLIC_DNS_CONFIG + applications: + - type: c2-server # Represents the external internet. + options: + listen_on_ports: + - 80 + - 53 + - 21 + ######################## + # REMOTE SITE SUBNET # + ######################## + - hostname: REM-PUB-FW + type: firewall + ports: + external_port: # Public Internet facing + ip_address: 10.1.10.2 + subnet_mask: *REMOTE_INTERNET_SUBNET + internal_port: # Remote Site (DMZ Subnet) facing + ip_address: 192.168.10.1 + subnet_mask: *REMOTE_SUBNET_DMZ + routes: + - address: 192.168.20.0 # Remote Site Network + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 192.168.10.2 + default_route: + next_hop_ip_address: 10.1.10.1 # Forward to internet router port 3 + acl: # TODO: Expand on this (For now permit all) + internal_inbound_acl: + 1: + action: PERMIT + internal_outbound_acl: + 1: + action: PERMIT + dmz_inbound_acl: + 1: + action: PERMIT + dmz_outbound_acl: + 1: + action: PERMIT + external_inbound_acl: + 1: + action: PERMIT + external_inbound_acl: + 1: + action: PERMIT + + - hostname: REM-PUB-RT-DR + type: router + default_gateway: 192.168.10.1 + ports: + 1: + ip_address: 192.168.10.2 + subnet_mask: *REMOTE_SUBNET_DMZ + 2: + ip_address: 192.168.20.1 + subnet_mask: *REMOTE_SUBNET + default_route: + next_hop_ip_address: 192.168.10.1 + acl: + 5: + action: PERMIT + + - hostname: REM-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: REM-PUB-PC-1 + type: computer + ip_address: 192.168.20.2 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-PC-2 + type: computer + ip_address: 192.168.20.3 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-SRV + type: server + ip_address: 192.168.20.4 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + + + ######################## + # SOME_TECH DMZ SUBNET # + ######################## + + - hostname: ST-PUB-FW + type: firewall + ports: + external_port: # Public Internet Facing Port + ip_address: 10.1.100.2 + subnet_mask: *ST_INTERNET_SUBNET + internal_port: # SOME_TECH Intranet Port + ip_address: 192.168.150.1 + subnet_mask: *ST_INTRA_CR_SUBNET + dmz_port: # SOME_TECH Port Facing Port + ip_address: 192.168.100.1 + subnet_mask: *ST_DMZ_SUBNET + acl: # TODO: Expand on this (For now permit all) + internal_inbound_acl: + 5: + action: PERMIT + internal_outbound_acl: + 5: + action: PERMIT + dmz_inbound_acl: + 5: + action: PERMIT + dmz_outbound_acl: + 5: + action: PERMIT + external_inbound_acl: + 5: + action: PERMIT + external_inbound_acl: + 5: + action: PERMIT + routes: + - address: 10.1.100.0 + subnet_mask: *ST_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.10.0 + subnet_mask: *REMOTE_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.0.0 + subnet_mask: *HOME_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 10.1.100.1 + + # - address: 192.168.150.0 + # subnet_mask: *ST_INTRA_CR_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.160.0 + # subnet_mask: *ST_INTRA_DR_ONE_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.170.0 + # subnet_mask: *ST_INTRA_DR_TWO_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.200.0 + # subnet_mask: *ST_HO_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.210.0 + # subnet_mask: *ST_HR_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.220.0 + # subnet_mask: *ST_DATA_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.230.0 + # subnet_mask: *ST_PROJ_A_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.240.0 + # subnet_mask: *ST_PROJ_B_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.250.0 + # subnet_mask: *ST_PROJ_C_SUBNET + # next_hop_ip_address: 192.168.150.2 + default_route: + #next_hop_ip_address: 10.1.100.1 + next_hop_ip_address: 192.168.150.2 + + - hostname: ST-DMZ-PUB-SRV-WEB + type: server + ip_address: *ST_PUB_SRV_WEB_IP + subnet_mask: *ST_DMZ_SUBNET + default_gateway: 192.168.100.1 + services: + *ST_SRV_WEB_CONFIG + + ############################# + # SOME_TECH INTRANET SUBNET # + ############################# + + - hostname: ST-INTRA-PRV-RT-CR + type: router + ports: + 1: + ip_address: 192.168.150.2 + subnet_mask: *ST_INTRA_CR_SUBNET + 2: + ip_address: 192.168.160.1 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 3: + ip_address: 192.168.170.1 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 4: + ip_address: 192.168.220.1 + subnet_mask: *ST_DATA_SUBNET + + routes: + - address: 192.168.200.0 + subnet_mask: *ST_HO_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.210.0 + subnet_mask: *ST_HR_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.230.0 + subnet_mask: *ST_PROJ_A_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.240.0 + subnet_mask: *ST_PROJ_B_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.250.0 + subnet_mask: *ST_PROJ_C_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + + default_route: + next_hop_ip_address: 192.168.150.1 # ST Public Firewall Internal Port + acl: + 5: + action: PERMIT + + - hostname: ST-INTRA-PRV-RT-DR-1 + type: router + ports: + 1: + ip_address: 192.168.160.2 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 2: + ip_address: 192.168.230.1 + subnet_mask: *ST_PROJ_A_SUBNET + 3: + ip_address: 192.168.240.1 + subnet_mask: *ST_PROJ_B_SUBNET + 4: + ip_address: 192.168.250.1 + subnet_mask: *ST_PROJ_C_SUBNET + default_route: + next_hop_ip_address: 192.168.160.1 # ST Intranet CR Router Port 2 + acl: + 5: + action: PERMIT + + - hostname: ST-INTRA-PRV-RT-DR-2 + type: router + default_gateway: 192.168.170.1 + ports: + 1: + ip_address: 192.168.170.2 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 2: + ip_address: 192.168.200.1 + subnet_mask: *ST_HO_SUBNET + 3: + ip_address: 192.168.210.1 + subnet_mask: *ST_HR_SUBNET + default_route: + next_hop_ip_address: 192.168.170.1 # ST Intranet CR Router Port 3 + acl: + 5: + action: PERMIT + + ################################ + # SOME_TECH HEAD OFFICE SUBNET # + ################################ + + - hostname: ST-HO-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-HO-PRV-PC-1 + type: computer + ip_address: 192.168.200.2 + default_gateway: 192.168.200.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HO-PRV-PC-2 + type: computer + ip_address: 192.168.200.3 + default_gateway: 192.168.200.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HO-PRV-PC-3 + type: computer + ip_address: 192.168.200.4 + default_gateway: 192.168.200.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + #################################### + # SOME_TECH HUMAN RESOURCES SUBNET # + #################################### + + - hostname: ST-HR-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-HR-PRV-PC-1 + type: computer + ip_address: 192.168.210.2 + default_gateway: 192.168.210.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HR-PRV-PC-2 + type: computer + ip_address: 192.168.210.3 + default_gateway: 192.168.210.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HR-PRV-PC-3 + type: computer + ip_address: 192.168.210.4 + default_gateway: 192.168.210.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ######################### + # SOME_TECH DATA SUBNET # + ######################### + + - hostname: ST-DATA-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-DATA-PRV-SRV-STORAGE + type: server + ip_address: *ST_SRV_STORAGE_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_STORAGE_CONFIG + + - hostname: ST-DATA-PRV-SRV-DB + type: server + ip_address: *ST_SRV_DB_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_DB_CONFIG + + ####################### + # SOME_TECH PROJECT A # + ####################### + + - hostname: ST-PROJ-A-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-PROJ-A-PRV-PC-1 + type: computer + ip_address: 192.168.230.2 + default_gateway: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-A-PRV-PC-2 + type: computer + ip_address: 192.168.230.3 + default_gateway: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-A-PRV-PC-3 + type: computer + ip_address: 192.168.230.4 + default_gateway: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT B # + ####################### + + - hostname: ST-PROJ-B-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-PROJ-B-PRV-PC-1 + type: computer + ip_address: 192.168.240.2 + default_gateway: 192.168.240.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-B-PRV-PC-2 + type: computer + ip_address: 192.168.240.3 + default_gateway: 192.168.240.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-B-PRV-PC-3 + type: computer + ip_address: 192.168.240.4 + default_gateway: 192.168.240.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT C # + ####################### + + - hostname: ST-PROJ-C-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-PROJ-C-PRV-PC-1 + type: computer + ip_address: 192.168.250.2 + default_gateway: 192.168.250.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-C-PRV-PC-2 + type: computer + ip_address: 192.168.250.3 + default_gateway: 192.168.250.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-C-PRV-PC-3 + type: computer + ip_address: 192.168.250.4 + default_gateway: 192.168.250.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + + ############################## + # Simulation Network Links # + ############################## + links: + + ############################ + # HOME OFFICE SUBNET LINKS # + ############################ + + # Home Switch (Port 1) --> Home Router (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 1 + + # Home Switch (Port 2) --> PC 1 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: HOME-PUB-PC-1 + endpoint_b_port: 1 + + # Home Switch (Port 3) --> PC 2 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: HOME-PUB-PC-2 + endpoint_b_port: 1 + + # Home Switch (Port 4) --> PC SRV (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: HOME-PUB-SRV + endpoint_b_port: 1 + + ################## + # Internet Links # + ################## + + # Internet Router (Port 1) --> Home Router (Port 2) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 2 + + # Internet Router (Port 2) --> DNS Server (Port 1) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 2 + endpoint_b_hostname: ISP-PUB-SRV-DNS + endpoint_b_port: 1 + + # Internet Router (Port 3) --> Remote Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-FW + endpoint_b_port: 1 + + #################### + # Remote DMZ Links # + #################### + + # Remote Firewall (Internal Port) --> Remote Site Router (Port 1) + - endpoint_a_hostname: REM-PUB-FW + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-RT-DR + endpoint_b_port: 1 + + #################### + # Remote Site Link # + #################### + + # Remote Site Router (Port 2) --> Remote Site Switch (Port 1) + - endpoint_a_hostname: REM-PUB-RT-DR + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-SW-AS + endpoint_b_port: 1 + + # Remote Site Switch (Port 2) --> Remote Site PC 1 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-PC-1 + endpoint_b_port: 1 + + # Remote Site Switch (Port 3) --> Remote Site PC 2 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-PC-2 + endpoint_b_port: 1 + + # Remote Site Switch (Port 4) --> Remote Site Server (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: REM-PUB-SRV + endpoint_b_port: 1 + + ####################### + # SOME_TECH DMZ Links # + ####################### + + # Internet Router (Port 4) --> Some Tech DMZ Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 4 + endpoint_b_hostname: ST-PUB-FW + endpoint_b_port: 1 + + # Some Tech DMZ Firewall (DMZ Port) --> Some Tech Web Server (Port 1) + - endpoint_a_hostname: ST-PUB-FW + endpoint_a_port: 3 + endpoint_b_hostname: ST-DMZ-PUB-SRV-WEB + endpoint_b_port: 1 + + ############################ + # SOME_TECH INTRANET Links # + ############################ + + # Some Tech Intranet CR Router (Port 1) --> Some Tech DMZ Firewall (Internal Port) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 1 + endpoint_b_hostname: ST-PUB-FW + endpoint_b_port: 2 + + # Some Tech Intranet CR Router (Port 2) --> Some Tech Intranet DR Router 1 (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 2 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_b_port: 1 + + # Some Tech Intranet CR Router (Port 3) --> Some Tech Intranet DR Router 2 (Port 2) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 3 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-2 + endpoint_b_port: 1 + + # Some Tech Intranet Private Router CR (Port 4) --> Some Tech Data Private Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 4 + endpoint_b_hostname: ST-DATA-PRV-SW-AS + endpoint_b_port: 1 + + + ############################### + # SOME_TECH HEAD OFFICE Links # + ############################### + + # Some Tech Head Office Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 2) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-2 + endpoint_b_port: 2 + + # Some Tech Head Office Switch (Port 2) --> Some Tech Head Office PC 1 (Port 1) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-HO-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 3) --> Some Tech Head Office PC 2 (Port 1) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-HO-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 4) --> Some Tech Head Office PC 3 (Port 1) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-HO-PRV-PC-3 + endpoint_b_port: 1 + + + ################################### + # SOME_TECH HUMAN RESOURCES Links # + ################################### + + # Some Tech Human Resources Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 3) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-2 + endpoint_b_port: 3 + + # Some Tech Human Resources Switch (Port 2) --> Some Tech Human Resources PC 1 (Port 1) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-HR-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 3) --> Some Tech Human Resources PC 2 (Port 1) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-HR-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 4) --> Some Tech Human Resources PC 3 (Port 1) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-HR-PRV-PC-3 + endpoint_b_port: 1 + + ######################## + # SOME_TECH DATA Links # + ######################## + + # Some Tech Data Switch (Port 2) --> Some Tech Data Private Storage Server (Port 1) + - endpoint_a_hostname: ST-DATA-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-DATA-PRV-SRV-STORAGE + endpoint_b_port: 1 + + # Some Tech Data Switch (Port 3) --> Some Tech Data Private Database Server (Port 1) + + - endpoint_a_hostname: ST-DATA-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-DATA-PRV-SRV-DB + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT A Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 2) --> Some Tech Private Project A Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 2) --> Some Tech Project A PC 1 + - endpoint_a_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-A-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 3) --> Some Tech Project A PC 2 + - endpoint_a_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-A-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 4) --> Some Tech Project A PC 3 + - endpoint_a_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-A-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT B Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 3) --> Some Tech Private Project B Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 2) --> Some Tech Project B PC 1 + - endpoint_a_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-B-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 3) --> Some Tech Project B PC 2 + - endpoint_a_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-B-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 4) --> Some Tech Project B PC 3 + - endpoint_a_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-B-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT C Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 4) --> Some Tech Private Project C Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 2) --> Some Tech Project C PC 1 + - endpoint_a_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-C-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 3) --> Some Tech Project C PC 2 + - endpoint_a_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-C-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 4) --> Some Tech Project C PC 3 + - endpoint_a_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-C-PRV-PC-3 + endpoint_b_port: 1 + +################################## +# Use Case 7 Agent YAML Anchors # +################################## + +############################## +# Green Agent YAML Anchors # +############################## + +# Green Agent Reward Impacts Values # + +LOW_WEIGHT_IMPACT: &LOW_WEIGHT_IMPACT 0.2 +MEDIUM_WEIGHT_IMPACT: &MEDIUM_WEIGHT_IMPACT 0.5 +HIGH_WEIGHT_IMPACT: &HIGH_WEIGHT_IMPACT 0.95 + +LOW_WEIGHT_IMPACT_NEGATIVE: &LOW_WEIGHT_IMPACT_NEG -0.2 +MEDIUM_WEIGHT_IMPACT_NEGATIVE: &MEDIUM_WEIGHT_IMPACT_NEG -0.5 +HIGH_WEIGHT_IMPACT_NEGATIVE: &HIGH_WEIGHT_IMPACT_NEG -0.8 + +# Default Green Agent Action Space Configuration Anchor # + +DEFAULT_GREEN_AGENT_MAX_EXECUTIONS: &DEFAULT_GREEN_AGENT_MAX_EXECUTIONS 1000 # Ensures green agent activity through-out an episode + +################################################# +# Probabilistic Green Agent Config Yaml Anchors # +################################################# + +# Probabilistic Green Agent | 20% NODE_APPLICATION_EXECUTE | 80% do-nothing # +PROBABILISTIC_CONFIG_20_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_20 + action_probabilities: + 0: 0.8 + 1: 0.2 + +# Probabilistic Green Agent | 40% NODE_APPLICATION_EXECUTE | 60% do-nothing # +PROBABILISTIC_CONFIG_40_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_40 + action_probabilities: + 0: 0.6 + 1: 0.4 + +# Probabilistic Green Agent | 60% NODE_APPLICATION_EXECUTE | 40% do-nothing # +PROBABILISTIC_CONFIG_20_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_60 + action_probabilities: + 0: 0.4 + 1: 0.6 + + +# System Green Agent Config UC7 Network Wide Yaml Anchor # + +# Lists the IP_Address of all hosts that contain DNS and NTP Service Clients +UC7_IP_LIST: &UC7_IP_LIST + # ====== Home Office ======== + - 192.168.1.2 # HOME-PUB-PC-1 | ip_id: 2 + - 192.168.1.3 # HOME-PUB-PC-2 | ip_id: 3 + - 192.168.1.4 # HOME-PUB-PC-SRV | ip_id: 4 + # ====== Remote Site ======== + - 192.168.20.2 # REM-PUB-PC-1 | ip_id: 5 + - 192.168.20.3 # REM-PUB-PC-2 | ip_id: 6 + - 192.168.20.4 # REM-PUB-SRV | ip_id: 7 + # ====== ST Public DMZ ======= + - *ST_PUB_SRV_WEB_IP # 192.168.100.2 (ST-DMZ-PUB-SRV-WEB) | ip_id: 8 + # ====== ST Head Office ======= + - 192.168.200.2 # ST-HO-PRV-PC-1 | ip_id: 9 + - 192.168.200.3 # ST-HO-PRV-PC-2 | ip_id: 10 + - 192.168.200.4 # ST-HO-PRV-PC-3 | ip_id: 11 + # ===== ST Human Resources ====== + - 192.168.210.2 # ST-HR-PRV-PC-1 | ip_id: 12 + - 192.168.210.3 # ST-HR-PRV-PC-2 | ip_id: 13 + - 192.168.210.4 # ST-HR-PRV-PC-3 | ip_id: 14 + # ====== ST DATA Servers ======= + - *ST_SRV_STORAGE_IP # 192.168.220.2 (ST-DATA-PRV-SRV-STORAGE) | ip_id: 15 + - *ST_SRV_DB_IP # 192.168.220.3 (ST-DATA-PRV-SRV-DB) | ip_id: 16 + # ====== ST Project A ======= + - 192.168.230.2 # PROJ-A-PRV-PC-1 | ip_id: 17 + - 192.168.230.3 # PROJ-A-PRV-PC-2 | ip_id: 18 + - 192.168.230.4 # PROJ-A-PRV-PC-3 | ip_id: 19 + # ====== ST Project B ======= + - 192.168.240.2 # PROJ-B-PRV-PC-1 | ip_id: 20 + - 192.168.240.3 # PROJ-B-PRV-PC-2 | ip_id: 21 + - 192.168.240.4 # PROJ-B-PRV-PC-3 | ip_id: 22 + # ====== ST Project C ======= + - 192.168.250.2 # PROJ-C-PRV-PC-1 | ip_id: 23 + - 192.168.250.3 # PROJ-C-PRV-PC-2 | ip_id: 24 + - 192.168.250.4 # PROJ-C-PRV-PC-3 | ip_id: 25 + +############################################ +# Use Case 7 Agent Configuration Section # +############################################ + +agents: + ####################################################### + # UC7 Green Agents Path of Life (POL) Configuration # + ####################################################### + + + #################################### + # Home Office Network POL Config # + #################################### + + # Home Office Green Agent Pattern Of Life + # ====================================== + # 1. Three Home workers accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. Three Home workers accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: HOME_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-1"] + target_application: "database-client" + start_step: 4 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-2"] + target_application: "database-client" + start_step: 8 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + - ref: HOME_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + #################################### + # Remote Site Network POL Config # + #################################### + + # Remote Site Green Agent Pattern Of Life + # ====================================== + # 1. Three Remote workers accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. Three Remote workers accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: REMOTE_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-1"] + target_application: "database-client" + start_step: 12 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-2"] + target_application: "database-client" + start_step: 16 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + - ref: REMOTE_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + #################################### + # ST Project A Network POL Config # + #################################### + + # ST Project A Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + + - ref: PROJ_A-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-A-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-1 + + - ref: PROJ_A-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-1 + + - ref: PROJ_A-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-A-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-A-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-A-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-3 + + - ref: PROJ_A-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-A-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-3 + + #################################### + # ST Project B Network POL Config # + #################################### + + # ST Project B Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: PROJ_B-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-B-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-1 + + - ref: PROJ_B-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-B-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-1 + + - ref: PROJ_B-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-B-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-B-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-3 + + - ref: PROJ_B-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-B-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-3 + + #################################### + # ST Project C Network POL Config # + #################################### + + # ST Project C Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: PROJ_C-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-C-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-1 + + - ref: PROJ_C-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-C-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-1 + + - ref: PROJ_C-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-C-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-C-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-C-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-3 + + - ref: PROJ_C-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-3 + + ###################################### + # ST Head Office Network POL Config # + ###################################### + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. The ST CEO accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 2. The ST CTO accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. The ST CFO accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: CEO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HO-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST-HO-PRV-PC-1 + + - ref: CTO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HO-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HO-PRV-PC-2 + + - ref: CFO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HO-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HO-PRV-PC-3 + + ########################################## + # ST Human Resources Network POL Config # + ########################################## + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. A senior HR staff accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 2. Two junior HR staff accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: SENIOR_HR + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HR-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HR-PRV-PC-1 + + - ref: JUNIOR_HR-1 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HR-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HR-PRV-PC-2 + + - ref: JUNIOR_HR-2 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HR-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HR-PRV-PC-3 + + ########################## + # UC7 Red Agent Config # + ########################## + + - ref: attacker + team: RED + type: tap-003 + observation_space: {} + action_space: {} + agent_settings: + start_step: 1 + frequency: 3 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_starting_node: "ST-PROJ-A-PRV-PC-1" + starting_nodes: + # starting_nodes: ["ST-PROJ-A-PRV-PC-1", "ST-PROJ-B-PRV-PC-2", "ST-PROJ-C-PRV-PC-3"] + kill_chain: + PLANNING: + probability: 1 + starting_network_knowledge: + credentials: + ST-PROJ-A-PRV-PC-1: + username: admin + password: admin + ST-PROJ-B-PRV-PC-2: + username: admin + password: admin + ST-PROJ-C-PRV-PC-3: + username: admin + password: admin + ST-INTRA-PRV-RT-DR-1: + ip_address: 192.168.230.1 + username: admin + password: admin + ST-INTRA-PRV-RT-CR: + ip_address: 192.168.160.1 + username: admin + password: admin + REM-PUB-RT-DR: + ip_address: 192.168.10.2 + username: admin + password: admin + ACCESS: + probability: 1 + MANIPULATION: + probability: 1 + account_changes: + - host: ST-INTRA-PRV-RT-DR-1 + ip_address: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 + action: change_password + username: admin + new_password: "red_pass" + - host: ST-INTRA-PRV-RT-CR + ip_address: 192.168.160.1 # ST-INTRA-PRV-RT-CR + action: change_password + username: "admin" + new_password: "red_pass" + - host: REM-PUB-RT-DR + ip_address: 192.168.10.2 # REM-PUB-RT-DR + action: change_password + username: "admin" + new_password: "red_pass" + EXPLOIT: + probability: 1 + malicious_acls: + - target_router: ST-INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_port: POSTGRES_SERVER + protocol_name: TCP + - target_router: ST-INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: HTTP + dst_port: HTTP + protocol_name: TCP + - target_router: REM-PUB-RT-DR + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: DNS + dst_port: DNS + protocol_name: TCP + +########################### +# UC7 Blue Agent Config # +########################### + + - ref: defender + team: BLUE + type: proxy-agent + observation_space: + type: custom + options: + components: + - type: nodes + label: NODES + options: + hosts: + # TAP001 Potential Starting Note | ST-PROJ-A-PRV-PC-1 + - hostname: ST-PROJ-A-PRV-PC-1 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST-PROJ-B-PRV-PC-2 + - hostname: ST-PROJ-B-PRV-PC-2 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST-PROJ-C-PRV-PC-3 + - hostname: ST-PROJ-C-PRV-PC-3 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # ST DATA Server Database + - hostname: ST-DATA-PRV-SRV-DB + folders: + - folder_name: database + files: + - file_name: database.db + routers: + - hostname: ST-INTRA-PRV-RT-CR # TAP003 Malicious ACL Target | ROUTER0 + - hostname: ST-INTRA-PRV-RT-DR-1 # TAP003 Malicious ACL Target | ROUTER1 + - hostname: REM-PUB-RT-DR # TAP003 Malicious ACL Target | ROUTER2 + num_ports: 5 + num_services: 2 + num_applications: 2 + num_folders: 2 + num_files: 1 + num_nics: 1 + ip_list: + # ip_list is indexed at 2: + # 0 reserved for padding to align with observations + # 1 reserved for ALL ips + *UC7_IP_LIST + wildcard_list: + - 0.0.0.1 # 0 + - 0.0.0.255 # 1 + - 0.0.255.255 # 2 + port_list: + # 0 is a padding placeholder + # 1 means ALL ports + - FTP # 2 + - DNS # 3 + - HTTP # 4 + - NTP # 5 + - POSTGRES_SERVER # 6 + - SSH # 7 + + protocol_list: + # 0 is padding placeholder + # 1 means ALL protocols + - ICMP # 2 + - TCP # 3 + - UDP # 4 + num_rules: 5 + include_num_access: true + include_nmne: true + monitored_traffic: + tcp: + - HTTP + - POSTGRES_SERVER + icmp: + - NONE + - type: links + label: LINKS + options: + link_references: + # HOME OFFICE SUBNET LINKS + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-RT-DR:eth-1 # 1 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-1:eth-1 # 2 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-2:eth-1 # 3 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-SRV:eth-1 # 4 + # Internet LINKS + - ISP-PUB-RT-BR:eth-1<->HOME-PUB-RT-DR:eth-2 # 5 + - ISP-PUB-RT-BR:eth-2<->ISP-PUB-SRV-DNS:eth-1 # 6 + - ISP-PUB-RT-BR:eth-3<->REM-PUB-FW:eth-1 # 7 + # Remote DMZ Links + - REM-PUB-FW:eth-2<->REM-PUB-RT-DR:eth-1 # 8 + # Remote Site Links + - REM-PUB-RT-DR:eth-2<->REM-PUB-SW-AS:eth-1 # 9 + - REM-PUB-SW-AS:eth-2<->REM-PUB-PC-1:eth-1 # 10 + - REM-PUB-SW-AS:eth-3<->REM-PUB-PC-2:eth-1 # 11 + - REM-PUB-SW-AS:eth-4<->REM-PUB-SRV:eth-1 # 12 + # SOME_TECH DMZ + - ISP-PUB-RT-BR:eth-4<->ST-PUB-FW:eth-1 # 13 + - ST-PUB-FW:eth-3<->ST-DMZ-PUB-SRV-WEB:eth-1 # 14 + # SOME_TECH Intranet + - ST-INTRA-PRV-RT-CR:eth-1<->ST-PUB-FW:eth-2 # 15 + - ST-INTRA-PRV-RT-CR:eth-2<->ST-INTRA-PRV-RT-DR-1:eth-1 # 16 + - ST-INTRA-PRV-RT-CR:eth-3<->ST-INTRA-PRV-RT-DR-2:eth-1 # 17 + - ST-INTRA-PRV-RT-CR:eth-4<->ST-DATA-PRV-SW-AS:eth-1 # 18 + # SOME_TECH Head Office + - ST-HO-PRV-SW-AS:eth-1<->ST-INTRA-PRV-RT-DR-2:eth-2 # 19 + - ST-HO-PRV-SW-AS:eth-2<->ST-HO-PRV-PC-1:eth-1 # 20 + - ST-HO-PRV-SW-AS:eth-3<->ST-HO-PRV-PC-2:eth-1 # 21 + - ST-HO-PRV-SW-AS:eth-4<->ST-HO-PRV-PC-3:eth-1 # 22 + # SOME_TECH Human Resources + - ST-HR-PRV-SW-AS:eth-1<->ST-INTRA-PRV-RT-DR-2:eth-3 # 23 + - ST-HR-PRV-SW-AS:eth-2<->ST-HR-PRV-PC-1:eth-1 # 24 + - ST-HR-PRV-SW-AS:eth-3<->ST-HR-PRV-PC-2:eth-1 # 25 + - ST-HR-PRV-SW-AS:eth-4<->ST-HR-PRV-PC-3:eth-1 # 26 + # SOME_TECH Data Links + - ST-DATA-PRV-SW-AS:eth-2<->ST-DATA-PRV-SRV-STORAGE:eth-1 # 27 + - ST-DATA-PRV-SW-AS:eth-3<->ST-DATA-PRV-SRV-DB:eth-1 # 28 + # SOME_TECH Project A Links + - ST-INTRA-PRV-RT-DR-1:eth-2<->ST-PROJ-A-PRV-SW-AS:eth-1 # 29 + - ST-PROJ-A-PRV-SW-AS:eth2<->ST-PROJ-A-PRV-PC-1:eth-1 # 31 + - ST-PROJ-A-PRV-SW-AS:eth3<->ST-PROJ-A-PRV-PC-2:eth-1 # 32 + - ST-PROJ-A-PRV-SW-AS:eth4<->ST-PROJ-A-PRV-PC-3:eth-1 # 33 + # SOME_TECH Project B Links + - ST-INTRA-PRV-RT-DR-1:eth-3<->ST-PROJ-B-PRV-SW-AS:eth-1 # 34 + - ST-PROJ-B-PRV-SW-AS:eth2<->ST-PROJ-B-PRV-PC-1:eth-1 # 35 + - ST-PROJ-B-PRV-SW-AS:eth3<->ST-PROJ-B-PRV-PC-2:eth-1 # 36 + - ST-PROJ-B-PRV-SW-AS:eth4<->ST-PROJ-B-PRV-PC-3:eth-1 # 37 + # SOME_TECH Project C Links + - ST-INTRA-PRV-RT-DR-1:eth-4<->ST-PROJ-C-PRV-SW-AS:eth-1 # 38 + - ST-PROJ-A-PRV-SW-AS:eth2<->ST-PROJ-C-PRV-PC-1:eth-1 # 39 + - ST-PROJ-A-PRV-SW-AS:eth3<->ST-PROJ-C-PRV-PC-2:eth-1 # 40 + - ST-PROJ-A-PRV-SW-AS:eth4<->ST-PROJ-C-PRV-PC-3:eth-1 # 41 + action_space: + action_map: + 0: + action: do-nothing + options: {} + + # |======================================| + # | ST-PROJ-A-PRV-PC-1 | + # |======================================| + + # ST-PROJ-A-PRV-PC-1 | node-os-scan + 1: + action: node-os-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | node-shutdown + 2: + action: node-shutdown + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | node-startup + 3: + action: node-startup + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | node-reset + 4: + action: node-reset + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | host-nic-disable + 5: + action: host-nic-disable + options: + node_name: ST-PROJ-A-PRV-PC-1 + nic_num: 0 + # ST-PROJ-A-PRV-PC-1 | host-nic-enable + 6: + action: host-nic-enable + options: + node_name: ST-PROJ-A-PRV-PC-1 + nic_num: 0 + # ST-PROJ-A-PRV-PC-1 | node-application-close | database-client + 7: + action: node-application-close + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + # ST-PROJ-A-PRV-PC-1 | node-application-scan | database-client + 8: + action: node-application-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + # ST-PROJ-A-PRV-PC-1 | node-application-fix | database-client + 9: + action: node-application-fix + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + # ST-PROJ-A-PRV-PC-1 | node-application-remove | database-client + 10: + action: node-application-remove + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + + # ST-PROJ-A-PRV-PC-1 | node-file-scan | downloads/malware_dropper.ps1 + 11: + action: node-file-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST-PROJ-A-PRV-PC-1 | node-file-scan | exfiltration_folder/database.db + 12: + action: node-file-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + file_name: database.db + + # ST-PROJ-A-PRV-PC-1 | node-folder-scan | downloads/ + 13: + action: node-folder-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: downloads + + # ST-PROJ-A-PRV-PC-1 | node-folder-scan | exfiltration_folder/ + 14: + action: node-folder-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + + # |======================================| + # | ST-PROJ-B-PRV-PC-2 | + # |======================================| + + # ST-PROJ-B-PRV-PC-2 | node-os-scan + 15: + action: node-os-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | node-shutdown + 16: + action: node-shutdown + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | node-startup + 17: + action: node-startup + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | node-reset + 18: + action: node-reset + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | host-nic-disable + 19: + action: host-nic-disable + options: + node_name: ST-PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST-PROJ-B-PRV-PC-2 | host-nic-enable + 20: + action: host-nic-enable + options: + node_name: ST-PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST-PROJ-B-PRV-PC-2 | node-application-close | database-client + 21: + action: node-application-close + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-application-scan | database-client + 22: + action: node-application-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-application-fix | database-client + 23: + action: node-application-fix + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-application-remove | database-client + 24: + action: node-application-remove + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-file-scan | downloads/malware_dropper.ps1 + 25: + action: node-file-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST-PROJ-B-PRV-PC-2 | node-file-scan | exfiltration_folder/database.db + 26: + action: node-file-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + file_name: database.db + + # ST-PROJ-B-PRV-PC-2 | node-folder-scan | downloads/ + 27: + action: node-folder-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: downloads + + # ST-PROJ-B-PRV-PC-2 | node-folder-scan | exfiltration_folder/ + 28: + action: node-folder-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + + # |======================================| + # | ST-PROJ-C-PRV-PC-3 | + # |======================================| + + # ST-PROJ-C-PRV-PC-3 | node-os-scan + 29: + action: node-os-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | node-shutdown + 30: + action: node-shutdown + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | node-startup + 31: + action: node-startup + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | node-reset + 32: + action: node-reset + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | host-nic-disable + 33: + action: host-nic-disable + options: + node_name: ST-PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST-PROJ-C-PRV-PC-3 | host-nic-enable + 34: + action: host-nic-enable + options: + node_name: ST-PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST-PROJ-C-PRV-PC-3 | node-application-close | database-client + 35: + action: node-application-close + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-application-scan | database-client + 36: + action: node-application-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-application-fix | database-client + 37: + action: node-application-fix + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-application-remove | database-client + 38: + action: node-application-remove + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-file-scan | downloads/malware_dropper.ps1 + 39: + action: node-file-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST-PROJ-C-PRV-PC-3 | node-file-scan | exfiltration_folder/database.db + 40: + action: node-file-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + file_name: database.db + + # ST-PROJ-C-PRV-PC-3 | node-folder-scan | downloads/ + 41: + action: node-folder-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: downloads + + # ST-PROJ-C-PRV-PC-3 | node-folder-scan | exfiltration_folder/ + 42: + action: node-folder-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + + # |======================================| + # | ST-INTRA-PRV-RT-CR | + # |======================================| + + # ST-INTRA-PRV-RT-CR | router-acl-addrule | P2: ST-PROJ-A-PRV-PC-1 !==> ST-DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 43: + action: router-acl-addrule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST-PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST-DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST-INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 1 + 44: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 1 + + # ST-INTRA-PRV-RT-CR | router-acl-addrule | P3: ST-PROJ-B-PRV-PC-2 !==> ST-DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 45: + action: router-acl-addrule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 2 + permission: DENY + src_ip: 192.168.240.3 # (ST-PROJ-B-PRV-PC-2) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST-DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + # ST-INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 2 + 46: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 2 + + # ST-INTRA-PRV-RT-CR | router-acl-addrule | P4: ST-PROJ-C-PRV-PC-3 !==> ST-DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 47: + action: router-acl-addrule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 3 + permission: DENY + src_ip: 192.168.250.4 # (ST-PROJ-C-PRV-PC-3) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST-DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST-INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 3 + 48: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 3 + + # |======================================| + # | ST-DATA-PRV-SRV-DB | + # |======================================| + + # ST-DATA-PRV-SRV-DB | node-file-scan | Scans the database.db file (health status) + 49: + action: node-file-scan + options: + node_name: ST-DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # ST-DATA-PRV-SRV-DB | node-account-change-password | Changes the password of a user account + 50: + action: node-account-change-password + options: + node_name: ST-DATA-PRV-SRV-DB + username: admin # default account + current_password: admin # default password + new_password: thr33_alert_wolv3z # A more 'secure' password + + # |======================================| + # | ST-INTRA-PRV-RT-DR-1 | + # |======================================| + + # ST-INTRA-PRV-RT-DR-1 | router-acl-addrule | P1: ST-INTRA-PRV-RT-DR-1 !==> ANY (TCP:SSH) + 51: + action: router-acl-addrule + options: + target_router: ST-INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST-PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: SSH + dst_ip: ALL + dst_wildcard: 0.0.255.255 + dst_port: SSH + protocol_name: TCP + + # ST-INTRA-PRV-RT-DR-1 | node-account-change-password + 52: + action: node-account-change-password + options: + node_name: ST-INTRA-PRV-RT-DR-1 + username: admin + current_password: admin + new_password: secure_password + + # ST-INTRA-PRV-RT-DR-1 | router-acl-remove-rule | Removes the given ACL at position 1 + 53: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-DR-1 + position: 1 + + # |======================================| + # | REM-PUB-RT-DR | + # |======================================| + + # REM-PUB-RT-DR | node-account-change-password + 54: + action: node-account-change-password + options: + node_name: REM-PUB-RT-DR + username: admin + current_password: admin + new_password: secure_password + + # REM-PUB-RT-DR | router-acl-remove-rule | Removes the given ACL at position 1 + 55: + action: router-acl-remove-rule + options: + target_router: REM-PUB-RT-DR + position: 1 + + reward_function: + reward_components: + - type: database-file-integrity + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST-DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # Home Site Green Agents (32 Green Agents each contributing 0.03125 of blue reward) + + # Blue Shared Reward | HOME_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-DB + + # Blue Shared Reward | HOME_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-WEB + + # Blue Shared Reward | HOME_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-DB + + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-WEB + + # Remote Site Green Agents + + # Blue Shared Reward | REMOTE_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-DB + + # Blue Shared Reward | REMOTE_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-WEB + + # Blue Shared Reward | REMOTE_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-DB + + # Blue Shared Reward | REMOTE_WORKER-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-WEB + + # ST Project A Green Agents + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-WEB + + # ST Project B Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Project C Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Head Office Green Agents (CEO/CFO/CTO) + + # Blue Shared Reward | CEO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CEO + + # Blue Shared Reward | CFO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CFO + + # Blue Shared Reward | CTO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CTO + + # ST Human Resources Green Agents + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: SENIOR_HR + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-1 + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-2 + + agent_settings: + flatten_obs: True + + + + # ####################################### + # # UC7 Network Wide System POL Config # + # ####################################### + + # # UC7 Network Wide System Pattern Of Life + # # ========================================== + # # 1. All NTP Clients perform periodic NTP Server transactions. + # # 2. Periodic Database (ST-DATA-PRV-SRV-DB) FTP backups (ST-DATA-PRV-SRV-STORAGE) + # # 3. All DNS Clients perform periodic DNS Server transactions. + + # # TODO: NTPClient Node Service Start doesn't start a 'Request' + + # # 1. All NTP Clients perform periodic NTP Server transactions. + + # - ref: SYSTEM-NTP + # team: GREEN + # type: probabilistic-agent + # observation_space: null + # action_space: + # action_list: + # - type: do-nothing + # - type: NODE_SERVICE_START + # action_map: + # # ====== Home Office ======== + # 0: + # action: do-nothing + # options: {} + # 1: + # action: NODE_SERVICE_START + # options: + # node_id: 0 # HOME-PUB-PC-1 + # service_id: 0 # NTPClient + # 2: + # action: NODE_SERVICE_START + # options: + # node_id: 1 # HOME-PUB-PC-2 + # service_id: 0 + # 3: + # action: NODE_SERVICE_START + # options: + # node_id: 2 # HOME-PUB-PC-SRV + # service_id: 0 + # # ====== Remote Site ======== + # 4: + # action: NODE_SERVICE_START + # options: + # node_id: 3 # REM-PUB-PC-1 + # service_id: 0 + # 5: + # action: NODE_SERVICE_START + # options: + # node_id: 4 # REM-PUB-PC-2 + # service_id: 0 + # 6: + # action: NODE_SERVICE_START + # options: + # node_id: 5 # REM-PUB-SRV + # service_id: 0 + # # ====== ST Public DMZ ======= + # 7: + # action: NODE_SERVICE_START + # options: + # node_id: 6 # ST-DMZ-PUB-SRV-WEB + # service_id: 0 + # # ====== ST Head Office ======= + # 8: + # action: NODE_SERVICE_START + # options: + # node_id: 7 # ST-HO-PRV-PC-1 + # service_id: 0 + # 9: + # action: NODE_SERVICE_START + # options: + # node_id: 8 # ST-HO-PRV-PC-2 + # service_id: 0 + # 10: + # action: NODE_SERVICE_START + # options: + # node_id: 9 # ST-HO-PRV-PC-3 + # service_id: 0 + # # ====== ST Human Resources ======= + # 11: + # action: NODE_SERVICE_START + # options: + # node_id: 10 # ST-HR-PRV-PC-1 + # service_id: 0 + # 12: + # action: NODE_SERVICE_START + # options: + # node_id: 11 # ST-HR-PRV-PC-2 + # service_id: 0 + # 13: + # action: NODE_SERVICE_START + # options: + # node_id: 12 # ST-HR-PRV-PC-3 + # service_id: 0 + # # ====== ST DATA Servers ======= + # 14: + # action: NODE_SERVICE_START + # options: + # node_id: 13 # ST-DATA-PRV-SRV-STORAGE + # service_id: 0 + # 15: + # action: NODE_SERVICE_START + # options: + # node_id: 14 # ST-DATA-PRV-SRV-DB + # service_id: 0 + # # ====== ST Project A ======= + # 16: + # action: NODE_SERVICE_START + # options: + # node_id: 15 # PROJ-A-PRV-PC-1 + # service_id: 0 + # 17: + # action: NODE_SERVICE_START + # options: + # node_id: 16 # PROJ-A-PRV-PC-2 + # service_id: 0 + # 18: + # action: NODE_SERVICE_START + # options: + # node_id: 17 # PROJ-A-PRV-PC-3 + # service_id: 0 + # # ====== ST Project B ======= + # 19: + # action: NODE_SERVICE_START + # options: + # node_id: 18 # PROJ-B-PRV-PC-1 + # service_id: 0 + # 20: + # action: NODE_SERVICE_START + # options: + # node_id: 19 # PROJ-B-PRV-PC-2 + # service_id: 0 + # 21: + # action: NODE_SERVICE_START + # options: + # node_id: 20 # PROJ-B-PRV-PC-3 + # service_id: 0 + # # ====== ST Project C ======= + # 22: + # action: NODE_SERVICE_START + # options: + # node_id: 21 # PROJ-C-PRV-PC-1 + # service_id: 0 + # 23: + # action: NODE_SERVICE_START + # options: + # node_id: 22 # PROJ-C-PRV-PC-2 + # service_id: 0 + # 24: + # action: NODE_SERVICE_START + # options: + # node_id: 23 # PROJ-C-PRV-PC-3 + # service_id: 0 + # options: + # nodes: + # # ====== Home Office ======== + # - node_name: HOME-PUB-PC-1 + # services: + # - service_name: NTPClient + # - node_name: HOME-PUB-PC-2 + # services: + # - service_name: NTPClient + # - node_name: HOME-PUB-SRV + # services: + # - service_name: NTPClient + # # ====== Remote Site ======== + # - node_name: REM-PUB-PC-1 + # services: + # - service_name: NTPClient + # - node_name: REM-PUB-PC-2 + # services: + # - service_name: NTPClient + # - node_name: REM-PUB-SRV + # services: + # - service_name: NTPClient + # # ====== ST Public DMZ ======= + # - node_name: ST-DMZ-PUB-SRV-WEB + # services: + # - service_name: NTPClient + # # ====== ST Head Office ======= + # - node_name: ST-HO-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: ST-HO-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: ST-HO-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ===== ST Human Resources ====== + # - node_name: ST-HR-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: ST-HR-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: ST-HR-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ====== ST DATA Servers ======= + # - node_name: ST-DATA-PRV-SRV-STORAGE + # services: + # - service_name: NTPClient + # - node_name: ST-DATA-PRV-SRV-DB + # services: + # - service_name: NTPClient + # # ====== ST Project A ======= + # - node_name: PROJ-A-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: PROJ-A-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: PROJ-A-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ====== ST Project B ======= + # - node_name: PROJ-B-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: PROJ-B-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: PROJ-B-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ====== ST Project C ======= + # - node_name: PROJ-C-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: PROJ-C-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: PROJ-C-PRV-PC-3 + # services: + # - service_name: NTPClient + # ip_list: + # *UC7_IP_LIST + # max_folders_per_node: 1 + # max_files_per_folder: 1 + # max_services_per_node: 1 + # max_applications_per_node: 1 + # settings: + # start_step: 1 + # start_variance: 0 + # max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + # frequency: 1 + # variance: 0 + # reward_function: + # reward_components: + # - type: DUMMY + + # # 2. Periodic Database (ST-DATA-PRV-SRV-DB) FTP backups (ST-DATA-PRV-SRV-STORAGE) + + # - ref: SYSTEM-FTP + # team: GREEN + # type: probabilistic-agent + # observation_space: null + # action_space: + # action_list: + # - type: do-nothing + # - type: NODE_SERVICE_FIX + # action_map: + # 0: + # action: do-nothing + # options: {} + # 1: + # action: NODE_SERVICE_FIX + # options: + # node_id: 0 + # service_id: 0 + # options: + # nodes: + # - node_name: ST-DATA-PRV-SRV-DB + # applications: + # - application_name: database-client + # ip_list: + # - *ST_SRV_DB_IP + # max_folders_per_node: 1 + # max_files_per_folder: 1 + # max_services_per_node: 1 + # max_applications_per_node: 1 + # settings: + # start_step: 4 + # start_variance: 0 + # max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + # frequency: 4 + # variance: 0 + # reward_function: + # reward_components: + # - type: DUMMY + + # # 3. All DNS Clients perform periodic DNS Server transactions. + + # # TODO: Address issue where dns-clients can't be started directly via agents + # - ref: SYSTEM-DNS + # team: GREEN + # type: probabilistic-agent + # observation_space: null + # action_space: + # action_list: + # - type: do-nothing + # - type: NODE_SERVICE_START + # action_map: + # # ====== Home Office ======== + # 0: + # action: do-nothing + # options: {} + # 1: + # action: NODE_SERVICE_START + # options: + # node_id: 0 # HOME-PUB-PC-1 + # service_id: 0 # dns-client + # 2: + # action: NODE_SERVICE_START + # options: + # node_id: 1 # HOME-PUB-PC-2 + # service_id: 0 + # 3: + # action: NODE_SERVICE_START + # options: + # node_id: 2 # HOME-PUB-PC-SRV + # service_id: 0 + # # ====== Remote Site ======== + # 4: + # action: NODE_SERVICE_START + # options: + # node_id: 3 # REM-PUB-PC-1 + # service_id: 0 + # 5: + # action: NODE_SERVICE_START + # options: + # node_id: 4 # REM-PUB-PC-2 + # service_id: 0 + # 6: + # action: NODE_SERVICE_START + # options: + # node_id: 5 # REM-PUB-SRV + # service_id: 0 + # # ====== ST Public DMZ ======= + # 7: + # action: NODE_SERVICE_START + # options: + # node_id: 6 # ST-DMZ-PUB-SRV-WEB + # service_id: 0 + # # ====== ST Head Office ======= + # 8: + # action: NODE_SERVICE_START + # options: + # node_id: 7 # ST-HO-PRV-PC-1 + # service_id: 0 + # 9: + # action: NODE_SERVICE_START + # options: + # node_id: 8 # ST-HO-PRV-PC-2 + # service_id: 0 + # 10: + # action: NODE_SERVICE_START + # options: + # node_id: 9 # ST-HO-PRV-PC-3 + # service_id: 0 + # # ====== ST Human Resources ======= + # 11: + # action: NODE_SERVICE_START + # options: + # node_id: 10 # ST-HR-PRV-PC-1 + # service_id: 0 + # 12: + # action: NODE_SERVICE_START + # options: + # node_id: 11 # ST-HR-PRV-PC-2 + # service_id: 0 + # 13: + # action: NODE_SERVICE_START + # options: + # node_id: 12 # ST-HR-PRV-PC-3 + # service_id: 0 + # # ====== ST DATA Servers ======= + # 14: + # action: NODE_SERVICE_START + # options: + # node_id: 13 # ST-DATA-PRV-SRV-STORAGE + # service_id: 0 + # 15: + # action: NODE_SERVICE_START + # options: + # node_id: 14 # ST-DATA-PRV-SRV-DB + # service_id: 0 + # # ====== ST Project A ======= + # 16: + # action: NODE_SERVICE_START + # options: + # node_id: 15 # PROJ-A-PRV-PC-1 + # service_id: 0 + # 17: + # action: NODE_SERVICE_START + # options: + # node_id: 16 # PROJ-A-PRV-PC-2 + # service_id: 0 + # 18: + # action: NODE_SERVICE_START + # options: + # node_id: 17 # PROJ-A-PRV-PC-3 + # service_id: 0 + # # ====== ST Project B ======= + # 19: + # action: NODE_SERVICE_START + # options: + # node_id: 18 # PROJ-B-PRV-PC-1 + # service_id: 0 + # 20: + # action: NODE_SERVICE_START + # options: + # node_id: 19 # PROJ-B-PRV-PC-2 + # service_id: 0 + # 21: + # action: NODE_SERVICE_START + # options: + # node_id: 20 # PROJ-B-PRV-PC-3 + # service_id: 0 + # # ====== ST Project C ======= + # 22: + # action: NODE_SERVICE_START + # options: + # node_id: 21 # PROJ-C-PRV-PC-1 + # service_id: 0 + # 23: + # action: NODE_SERVICE_START + # options: + # node_id: 22 # PROJ-C-PRV-PC-2 + # service_id: 0 + # 24: + # action: NODE_SERVICE_START + # options: + # node_id: 23 # PROJ-C-PRV-PC-3 + # service_id: 0 + # options: + # nodes: + # # ====== Home Office ======== + # - node_name: HOME-PUB-PC-1 + # services: + # - service_name: dns-client + # - node_name: HOME-PUB-PC-2 + # services: + # - service_name: dns-client + # - node_name: HOME-PUB-SRV + # services: + # - service_name: dns-client + # # ====== Remote Site ======== + # - node_name: REM-PUB-PC-1 + # services: + # - service_name: dns-client + # - node_name: REM-PUB-PC-2 + # services: + # - service_name: dns-client + # - node_name: REM-PUB-SRV + # services: + # - service_name: dns-client + # # ====== ST Public DMZ ======= + # - node_name: ST-DMZ-PUB-SRV-WEB + # services: + # - service_name: dns-client + # # ====== ST Head Office ======= + # - node_name: ST-HO-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: ST-HO-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: ST-HO-PRV-PC-3 + # services: + # - service_name: dns-client + # # ===== ST Human Resources ====== + # - node_name: ST-HR-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: ST-HR-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: ST-HR-PRV-PC-3 + # services: + # - service_name: dns-client + # # ====== ST DATA Servers ======= + # - node_name: ST-DATA-PRV-SRV-STORAGE + # services: + # - service_name: dns-client + # - node_name: ST-DATA-PRV-SRV-DB + # services: + # - service_name: dns-client + # # ====== ST Project A ======= + # - node_name: PROJ-A-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: PROJ-A-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: PROJ-A-PRV-PC-3 + # services: + # - service_name: dns-client + # # ====== ST Project B ======= + # - node_name: PROJ-B-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: PROJ-B-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: PROJ-B-PRV-PC-3 + # services: + # - service_name: dns-client + # # ====== ST Project C ======= + # - node_name: PROJ-C-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: PROJ-C-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: PROJ-C-PRV-PC-3 + # services: + # - service_name: dns-client + # ip_list: + # *UC7_IP_LIST + # max_folders_per_node: 1 + # max_files_per_folder: 1 + # max_services_per_node: 1 + # max_applications_per_node: 1 + # settings: + # start_step: 1 + # start_variance: 0 + # max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + # frequency: 1 + # variance: 0 + # reward_function: + # reward_components: + # - type: DUMMY diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC1.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC1.yaml new file mode 100644 index 00000000..a066eaa4 --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC1.yaml @@ -0,0 +1,43 @@ +red: &red + - ref: attacker + team: RED + type: tap-001 + agent_settings: + start_step: 1 + frequency: 5 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_target_ip: 192.168.220.3 + default_starting_node: "ST-PROJ-C-PRV-PC-1" + # starting_nodes: ["ST-PROJ-A-PRV-PC-1", "ST-PROJ-B-PRV-PC-2", "ST-PROJ-C-PRV-PC-3"] + starting_nodes: + kill_chain: + ACTIVATE: + probability: 1 + PROPAGATE: + probability: 1 + scan_attempts: 20 + repeat_scan: false + network_addresses: + - 192.168.230.0/29 # ST Project A + - 192.168.10.0/26 # Remote Site + - 192.168.20.0/30 # Remote DMZ + # - 192.168.240.0/29 # ST Project B + # - 192.168.250.0/29 # ST Project C + - 192.168.220.0/29 # ST Data (Contains Target) + COMMAND_AND_CONTROL: + probability: 1 + keep_alive_frequency: 5 + masquerade_port: HTTP + masquerade_protocol: TCP + c2_server_name: ISP-PUB-SRV-DNS + c2_server_ip: 8.8.8.8 + PAYLOAD: + probability: 1 + exfiltrate: true + corrupt: true + exfiltration_folder_name: + target_username: admin + target_password: admin + continue_on_failed_exfil: True diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC2.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC2.yaml new file mode 100644 index 00000000..7256b154 --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC2.yaml @@ -0,0 +1,43 @@ +red: &red + - ref: attacker + team: RED + type: tap-001 + agent_settings: + start_step: 1 + frequency: 5 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_target_ip: 192.168.220.3 + default_starting_node: "ST-PROJ-B-PRV-PC-2" + # starting_nodes: ["ST-PROJ-A-PRV-PC-1", "ST-PROJ-B-PRV-PC-2", "ST-PROJ-C-PRV-PC-3"] + starting_nodes: + kill_chain: + ACTIVATE: + probability: 1 + PROPAGATE: + probability: 1 + scan_attempts: 20 + repeat_scan: false + network_addresses: + - 192.168.230.0/29 # ST Project A + - 192.168.10.0/26 # Remote Site + - 192.168.20.0/30 # Remote DMZ + # - 192.168.240.0/29 # ST Project B + # - 192.168.250.0/29 # ST Project C + - 192.168.220.0/29 # ST Data (Contains Target) + COMMAND_AND_CONTROL: + probability: 1 + keep_alive_frequency: 5 + masquerade_port: HTTP + masquerade_protocol: TCP + c2_server_name: ISP-PUB-SRV-DNS + c2_server_ip: 8.8.8.8 + PAYLOAD: + probability: 1 + exfiltrate: true + corrupt: true + exfiltration_folder_name: + target_username: admin + target_password: admin + continue_on_failed_exfil: True diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC3.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC3.yaml new file mode 100644 index 00000000..162f4753 --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC3.yaml @@ -0,0 +1,43 @@ +red: &red + - ref: attacker + team: RED + type: tap-001 + agent_settings: + start_step: 1 + frequency: 5 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_target_ip: 192.168.220.3 + default_starting_node: "ST-PROJ-C-PRV-PC-3" + # starting_nodes: ["ST-PROJ-A-PRV-PC-1", "ST-PROJ-B-PRV-PC-2", "ST-PROJ-C-PRV-PC-3"] + starting_nodes: + kill_chain: + ACTIVATE: + probability: 1 + PROPAGATE: + probability: 1 + scan_attempts: 20 + repeat_scan: false + network_addresses: + - 192.168.230.0/29 # ST Project A + - 192.168.10.0/26 # Remote Site + - 192.168.20.0/30 # Remote DMZ + # - 192.168.240.0/29 # ST Project B + # - 192.168.250.0/29 # ST Project C + - 192.168.220.0/29 # ST Data (Contains Target) + COMMAND_AND_CONTROL: + probability: 1 + keep_alive_frequency: 5 + masquerade_port: HTTP + masquerade_protocol: TCP + c2_server_name: ISP-PUB-SRV-DNS + c2_server_ip: 8.8.8.8 + PAYLOAD: + probability: 1 + exfiltrate: true + corrupt: true + exfiltration_folder_name: + target_username: admin + target_password: admin + continue_on_failed_exfil: True diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP003.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP003.yaml new file mode 100644 index 00000000..7dd3fd2e --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP003.yaml @@ -0,0 +1,94 @@ +red: &red + - ref: attacker + team: RED + type: tap-003 + observation_space: {} + action_space: {} + agent_settings: + start_step: 1 + frequency: 3 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_starting_node: "ST-PROJ-A-PRV-PC-1" + starting_nodes: + # starting_nodes: ["ST-PROJ-A-PRV-PC-1", "ST-PROJ-B-PRV-PC-2", "ST-PROJ-C-PRV-PC-3"] + kill_chain: + PLANNING: + probability: 1 + starting_network_knowledge: + credentials: + ST-PROJ-A-PRV-PC-1: + username: admin + password: admin + ST-PROJ-B-PRV-PC-2: + username: admin + password: admin + ST-PROJ-C-PRV-PC-3: + username: admin + password: admin + ST-INTRA-PRV-RT-DR-1: + ip_address: 192.168.230.1 + username: admin + password: admin + ST-INTRA-PRV-RT-CR: + ip_address: 192.168.160.1 + username: admin + password: admin + REM-PUB-RT-DR: + ip_address: 192.168.10.2 + username: admin + password: admin + ACCESS: + probability: 1 + MANIPULATION: + probability: 1 + account_changes: + - host: ST-INTRA-PRV-RT-DR-1 + ip_address: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 + action: change_password + username: admin + new_password: "red_pass" + - host: ST-INTRA-PRV-RT-CR + ip_address: 192.168.160.1 # ST-INTRA-PRV-RT-CR + action: change_password + username: "admin" + new_password: "red_pass" + - host: REM-PUB-RT-DR + ip_address: 192.168.10.2 # REM-PUB-RT-DR + action: change_password + username: "admin" + new_password: "red_pass" + EXPLOIT: + probability: 1 + malicious_acls: + - target_router: ST-INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_port: POSTGRES_SERVER + protocol_name: TCP + - target_router: ST-INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: HTTP + dst_port: HTTP + protocol_name: TCP + - target_router: REM-PUB-RT-DR + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: DNS + dst_port: DNS + protocol_name: TCP diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/schedule.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/schedule.yaml new file mode 100644 index 00000000..e11c2fbf --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/schedule.yaml @@ -0,0 +1,42 @@ +base_scenario: uc7_config_no_red.yaml +schedule: + 0: + - TAP001_PC1.yaml + 1: + - TAP001_PC2.yaml + 2: + - TAP001_PC3.yaml + 3: + - TAP001_PC1.yaml + 4: + - TAP001_PC2.yaml + 5: + - TAP003.yaml + 6: + - TAP003.yaml + 7: + - TAP003.yaml + 8: + - TAP003.yaml + 9: + - TAP003.yaml + 10: + - TAP001_PC1.yaml + 11: + - TAP003.yaml + 12: + - TAP001_PC1.yaml + 13: + - TAP003.yaml + 14: + - TAP001_PC2.yaml + 15: + - TAP003.yaml + 16: + - TAP001_PC3.yaml + 17: + - TAP003.yaml + 18: + - TAP001_PC1.yaml + 19: + - TAP003.yaml diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/uc7_config_no_red.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/uc7_config_no_red.yaml new file mode 100644 index 00000000..a55dae0e --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/uc7_config_no_red.yaml @@ -0,0 +1,3210 @@ +########################################################## +# USE CASE 7 CONFIGURATION YAML FILE # +########################################################## + + +########################################## +# PrimAITE Game and Logging Settings # +########################################## + + +# PrimAITE I/O Settings # + +io_settings: + save_agent_actions: True + save_step_metadata: True + save_pcap_logs: false + save_sys_logs: True + # save_sys_logs: true + write_sys_log_to_terminal: false + + +# PrimAITE Game Settings # + +game: + max_episode_length: 128 + ports: + - FTP + - DNS + - HTTP + - NTP + - POSTGRES_SERVER + - SSH + protocols: + - ICMP + - TCP + - UDP + thresholds: + nmne: + high: 10 + medium: 5 + low: 0 + +############################################ +# PrimAITE Use Case 7 Simulation # +############################################ + +########################################## +# Configuration Variables (Yaml Anchors) # +########################################## + +# External Network Address List # + +DNS_SUBNET: &DNS_SUBNET 255.255.255.240 # | 8.8.8.0 / 28 +HOME_INTERNET_SUBNET: &HOME_INTERNET_SUBNET 255.255.255.252 # | 10.1.0.0 / 30 +REMOTE_INTERNET_SUBNET: &REMOTE_INTERNET_SUBNET 255.255.255.252 # | 10.1.10.0 / 30 +SOME_TECH_INTERNET_SUBNET: &ST_INTERNET_SUBNET 255.255.255.252 # | 10.1.100.0 / 30 +HOME_OFFICE_SUBNET: &HOME_SUBNET 255.255.255.0 # | 192.168.1.0 / 26 +REMOTE_SUBNET_DMZ: &REMOTE_SUBNET_DMZ 255.255.255.252 # | 192.168.10.0 / 30 +REMOTE_SUBNET: &REMOTE_SUBNET 255.255.255.240 # | 192.168.20.0 / 28 + +# SOME_TECH (ST) Network Address List # + +SOME_TECH_DMZ_SUBNET: &ST_DMZ_SUBNET 255.255.255.252 # | 192.168.100.0 / 30 +SOME_TECH_INTRANET_RT_CR_SUBNET: &ST_INTRA_CR_SUBNET 255.255.255.240 # | 192.168.150.0 / 28 +SOME_TECH_INTRANET_RT_DR_ONE_SUBNET: &ST_INTRA_DR_ONE_SUBNET 255.255.255.252 # | 192.168.160.0 / 30 +SOME_TECH_INTRANET_RT_DR_TWO_SUBNET: &ST_INTRA_DR_TWO_SUBNET 255.255.255.252 # | 192.168.170.0 / 30 +SOME_TECH_HEAD_OFFICE_SUBNET: &ST_HO_SUBNET 255.255.255.248 # | 192.168.200.0 / 29 +SOME_TECH_HUMAN_RESOURCES_SUBNET: &ST_HR_SUBNET 255.255.255.248 # | 192.168.210.0 / 29 +SOME_TECH_DATA_SUBNET: &ST_DATA_SUBNET 255.255.255.248 # | 192.168.220.0 / 29 +SOME_TECH_PROJECT_A_SUBNET: &ST_PROJ_A_SUBNET 255.255.255.248 # | 192.168.230.0 / 29 +SOME_TECH_PROJECT_B_SUBNET: &ST_PROJ_B_SUBNET 255.255.255.248 # | 192.168.240.0 / 29 +SOME_TECH_PROJECT_C_SUBNET: &ST_PROJ_C_SUBNET 255.255.255.248 # | 192.168.250.0 / 29 + +# Host & Server Configurations # + +# ST Public Web Server | web-server | ST-DMZ-PUB-SRV-WEB +SOME_TECH_PUBLIC_SERVER_WEB_IP_ADDRESS: &ST_PUB_SRV_WEB_IP 192.168.100.2 +SOME_TECH_PUBLIC_SERVER_WEB_CONFIG: &ST_SRV_WEB_CONFIG # + - type: web-server + +# ISP Public DNS | dns-server | ISP-PUB-SRV-DNS +PUBLIC_DNS_IP_ADDRESS: &PUBLIC_DNS_IP 8.8.8.8 +PUBLIC_DNS_CONFIG: &PUBLIC_DNS_CONFIG # + - type: dns-server + options: + domain_mapping: + some_tech.com: *ST_PUB_SRV_WEB_IP + +# ST Private Storage Server | ftp-server | ST-DATA-PRV-SRV-STORAGE +SOME_TECH_PRIVATE_SERVER_STORAGE_IP: &ST_SRV_STORAGE_IP 192.168.220.2 +SOME_TECH_PRIVATE_SERVER_STORAGE_CONFIG: &ST_SRV_STORAGE_CONFIG + - type: ftp-server + +# ST Private Database Server | database-client & ftp-client | ST-DATA-PRV-SRV-DB +SOME_TECH_PRIVATE_SERVER_DATABASE_IP: &ST_SRV_DB_IP 192.168.220.3 +SOME_TECH_PRIVATE_SERVER_DATABASE_CONFIG: &ST_SRV_DB_CONFIG + - type: database-service + options: + backup_server_ip: *ST_SRV_STORAGE_IP + - type: ftp-client + +# Default PC Configuration | Database Client & Web Server +PERSONAL_COMPUTER_DEFAULT_CONFIG: &PC_DEFAULT_CONFIG + - type: database-client + options: + db_server_ip: *ST_SRV_DB_IP + - type: web-browser + options: + target_url: http://some_tech.com + + +############################## +# Simulation Configuration # +############################## + +simulation: + defaults: + folder_scan_duration: 0 + folder_restore_duration: 3 + service_fix_duration: 2 + service_restart_duration: 2 + software_install_duration: 0 + node_start_up_duration: 3 + node_shut_down_duration: 3 + node_scan_duration: 8 + network: + nmne_config: + capture_nmne: true + nmne_capture_keywords: + - DELETE + - ENCRYPT + nodes: + ###################### + # HOME OFFICE SUBNET # + ###################### + - hostname: HOME-PUB-RT-DR + type: router + default_gateway: 10.1.0.1 + ports: + 1: + ip_address: 192.168.1.1 + subnet_mask: *HOME_SUBNET + 2: + ip_address: 10.1.0.2 + subnet_mask: *HOME_INTERNET_SUBNET + default_route: + next_hop_ip_address: 10.1.0.1 + acl: + 5: + action: PERMIT + + - hostname: HOME-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: HOME-PUB-PC-1 + type: computer + ip_address: 192.168.1.2 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-PC-2 + type: computer + ip_address: 192.168.1.3 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-SRV + type: server + ip_address: 192.168.1.4 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + + ###################### + # INTERNET SUBNET # + ###################### + - hostname: ISP-PUB-RT-BR + type: router + ports: + 1: + ip_address: 10.1.0.1 + subnet_mask: *HOME_INTERNET_SUBNET + 2: + ip_address: 8.8.8.1 + subnet_mask: *DNS_SUBNET + 3: + ip_address: 10.1.10.1 + subnet_mask: *REMOTE_INTERNET_SUBNET + 4: + ip_address: 10.1.100.1 + subnet_mask: *ST_INTERNET_SUBNET + routes: + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.0.2 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 8.8.8.8 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.10.2 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.10.2 + + default_route: + next_hop_ip_address: 10.1.100.2 # SOME_TECH Firewall + + acl: + 5: + action: PERMIT + + ################ + # DNS SUBNET # + ################ + - hostname: ISP-PUB-SRV-DNS + type: server + ip_address: 8.8.8.8 + subnet_mask: *DNS_SUBNET + default_gateway: 8.8.8.1 + services: + *PUBLIC_DNS_CONFIG + applications: + - type: c2-server # Represents the external internet. + options: + listen_on_ports: + - 80 + - 53 + - 21 + ######################## + # REMOTE SITE SUBNET # + ######################## + - hostname: REM-PUB-FW + type: firewall + ports: + external_port: # Public Internet facing + ip_address: 10.1.10.2 + subnet_mask: *REMOTE_INTERNET_SUBNET + internal_port: # Remote Site (DMZ Subnet) facing + ip_address: 192.168.10.1 + subnet_mask: *REMOTE_SUBNET_DMZ + routes: + - address: 192.168.20.0 # Remote Site Network + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 192.168.10.2 + default_route: + next_hop_ip_address: 10.1.10.1 # Forward to internet router port 3 + acl: # TODO: Expand on this (For now permit all) + internal_inbound_acl: + 1: + action: PERMIT + internal_outbound_acl: + 1: + action: PERMIT + dmz_inbound_acl: + 1: + action: PERMIT + dmz_outbound_acl: + 1: + action: PERMIT + external_inbound_acl: + 1: + action: PERMIT + external_inbound_acl: + 1: + action: PERMIT + + - hostname: REM-PUB-RT-DR + type: router + default_gateway: 192.168.10.1 + ports: + 1: + ip_address: 192.168.10.2 + subnet_mask: *REMOTE_SUBNET_DMZ + 2: + ip_address: 192.168.20.1 + subnet_mask: *REMOTE_SUBNET + default_route: + next_hop_ip_address: 192.168.10.1 + acl: + 5: + action: PERMIT + + - hostname: REM-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: REM-PUB-PC-1 + type: computer + ip_address: 192.168.20.2 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-PC-2 + type: computer + ip_address: 192.168.20.3 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-SRV + type: server + ip_address: 192.168.20.4 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + + + ######################## + # SOME_TECH DMZ SUBNET # + ######################## + + - hostname: ST-PUB-FW + type: firewall + ports: + external_port: # Public Internet Facing Port + ip_address: 10.1.100.2 + subnet_mask: *ST_INTERNET_SUBNET + internal_port: # SOME_TECH Intranet Port + ip_address: 192.168.150.1 + subnet_mask: *ST_INTRA_CR_SUBNET + dmz_port: # SOME_TECH Port Facing Port + ip_address: 192.168.100.1 + subnet_mask: *ST_DMZ_SUBNET + acl: # TODO: Expand on this (For now permit all) + internal_inbound_acl: + 5: + action: PERMIT + internal_outbound_acl: + 5: + action: PERMIT + dmz_inbound_acl: + 5: + action: PERMIT + dmz_outbound_acl: + 5: + action: PERMIT + external_inbound_acl: + 5: + action: PERMIT + external_inbound_acl: + 5: + action: PERMIT + routes: + - address: 10.1.100.0 + subnet_mask: *ST_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.10.0 + subnet_mask: *REMOTE_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.0.0 + subnet_mask: *HOME_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 10.1.100.1 + + # - address: 192.168.150.0 + # subnet_mask: *ST_INTRA_CR_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.160.0 + # subnet_mask: *ST_INTRA_DR_ONE_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.170.0 + # subnet_mask: *ST_INTRA_DR_TWO_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.200.0 + # subnet_mask: *ST_HO_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.210.0 + # subnet_mask: *ST_HR_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.220.0 + # subnet_mask: *ST_DATA_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.230.0 + # subnet_mask: *ST_PROJ_A_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.240.0 + # subnet_mask: *ST_PROJ_B_SUBNET + # next_hop_ip_address: 192.168.150.2 + # - address: 192.168.250.0 + # subnet_mask: *ST_PROJ_C_SUBNET + # next_hop_ip_address: 192.168.150.2 + default_route: + #next_hop_ip_address: 10.1.100.1 + next_hop_ip_address: 192.168.150.2 + + - hostname: ST-DMZ-PUB-SRV-WEB + type: server + ip_address: *ST_PUB_SRV_WEB_IP + subnet_mask: *ST_DMZ_SUBNET + default_gateway: 192.168.100.1 + services: + *ST_SRV_WEB_CONFIG + + ############################# + # SOME_TECH INTRANET SUBNET # + ############################# + + - hostname: ST-INTRA-PRV-RT-CR + type: router + ports: + 1: + ip_address: 192.168.150.2 + subnet_mask: *ST_INTRA_CR_SUBNET + 2: + ip_address: 192.168.160.1 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 3: + ip_address: 192.168.170.1 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 4: + ip_address: 192.168.220.1 + subnet_mask: *ST_DATA_SUBNET + + routes: + - address: 192.168.200.0 + subnet_mask: *ST_HO_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.210.0 + subnet_mask: *ST_HR_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.230.0 + subnet_mask: *ST_PROJ_A_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.240.0 + subnet_mask: *ST_PROJ_B_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.250.0 + subnet_mask: *ST_PROJ_C_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + + default_route: + next_hop_ip_address: 192.168.150.1 # ST Public Firewall Internal Port + acl: + 5: + action: PERMIT + + - hostname: ST-INTRA-PRV-RT-DR-1 + type: router + ports: + 1: + ip_address: 192.168.160.2 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 2: + ip_address: 192.168.230.1 + subnet_mask: *ST_PROJ_A_SUBNET + 3: + ip_address: 192.168.240.1 + subnet_mask: *ST_PROJ_B_SUBNET + 4: + ip_address: 192.168.250.1 + subnet_mask: *ST_PROJ_C_SUBNET + default_route: + next_hop_ip_address: 192.168.160.1 # ST Intranet CR Router Port 2 + acl: + 5: + action: PERMIT + + - hostname: ST-INTRA-PRV-RT-DR-2 + type: router + default_gateway: 192.168.170.1 + ports: + 1: + ip_address: 192.168.170.2 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 2: + ip_address: 192.168.200.1 + subnet_mask: *ST_HO_SUBNET + 3: + ip_address: 192.168.210.1 + subnet_mask: *ST_HR_SUBNET + default_route: + next_hop_ip_address: 192.168.170.1 # ST Intranet CR Router Port 3 + acl: + 5: + action: PERMIT + + ################################ + # SOME_TECH HEAD OFFICE SUBNET # + ################################ + + - hostname: ST-HO-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-HO-PRV-PC-1 + type: computer + ip_address: 192.168.200.2 + default_gateway: 192.168.200.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HO-PRV-PC-2 + type: computer + ip_address: 192.168.200.3 + default_gateway: 192.168.200.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HO-PRV-PC-3 + type: computer + ip_address: 192.168.200.4 + default_gateway: 192.168.200.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + #################################### + # SOME_TECH HUMAN RESOURCES SUBNET # + #################################### + + - hostname: ST-HR-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-HR-PRV-PC-1 + type: computer + ip_address: 192.168.210.2 + default_gateway: 192.168.210.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HR-PRV-PC-2 + type: computer + ip_address: 192.168.210.3 + default_gateway: 192.168.210.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-HR-PRV-PC-3 + type: computer + ip_address: 192.168.210.4 + default_gateway: 192.168.210.1 # ST-INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ######################### + # SOME_TECH DATA SUBNET # + ######################### + + - hostname: ST-DATA-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-DATA-PRV-SRV-STORAGE + type: server + ip_address: *ST_SRV_STORAGE_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_STORAGE_CONFIG + + - hostname: ST-DATA-PRV-SRV-DB + type: server + ip_address: *ST_SRV_DB_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_DB_CONFIG + + ####################### + # SOME_TECH PROJECT A # + ####################### + + - hostname: ST-PROJ-A-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-PROJ-A-PRV-PC-1 + type: computer + ip_address: 192.168.230.2 + default_gateway: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-A-PRV-PC-2 + type: computer + ip_address: 192.168.230.3 + default_gateway: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-A-PRV-PC-3 + type: computer + ip_address: 192.168.230.4 + default_gateway: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT B # + ####################### + + - hostname: ST-PROJ-B-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-PROJ-B-PRV-PC-1 + type: computer + ip_address: 192.168.240.2 + default_gateway: 192.168.240.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-B-PRV-PC-2 + type: computer + ip_address: 192.168.240.3 + default_gateway: 192.168.240.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-B-PRV-PC-3 + type: computer + ip_address: 192.168.240.4 + default_gateway: 192.168.240.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT C # + ####################### + + - hostname: ST-PROJ-C-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST-PROJ-C-PRV-PC-1 + type: computer + ip_address: 192.168.250.2 + default_gateway: 192.168.250.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-C-PRV-PC-2 + type: computer + ip_address: 192.168.250.3 + default_gateway: 192.168.250.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST-PROJ-C-PRV-PC-3 + type: computer + ip_address: 192.168.250.4 + default_gateway: 192.168.250.1 # ST-INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + + ############################## + # Simulation Network Links # + ############################## + links: + + ############################ + # HOME OFFICE SUBNET LINKS # + ############################ + + # Home Switch (Port 1) --> Home Router (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 1 + + # Home Switch (Port 2) --> PC 1 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: HOME-PUB-PC-1 + endpoint_b_port: 1 + + # Home Switch (Port 3) --> PC 2 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: HOME-PUB-PC-2 + endpoint_b_port: 1 + + # Home Switch (Port 4) --> PC SRV (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: HOME-PUB-SRV + endpoint_b_port: 1 + + ################## + # Internet Links # + ################## + + # Internet Router (Port 1) --> Home Router (Port 2) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 2 + + # Internet Router (Port 2) --> DNS Server (Port 1) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 2 + endpoint_b_hostname: ISP-PUB-SRV-DNS + endpoint_b_port: 1 + + # Internet Router (Port 3) --> Remote Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-FW + endpoint_b_port: 1 + + #################### + # Remote DMZ Links # + #################### + + # Remote Firewall (Internal Port) --> Remote Site Router (Port 1) + - endpoint_a_hostname: REM-PUB-FW + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-RT-DR + endpoint_b_port: 1 + + #################### + # Remote Site Link # + #################### + + # Remote Site Router (Port 2) --> Remote Site Switch (Port 1) + - endpoint_a_hostname: REM-PUB-RT-DR + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-SW-AS + endpoint_b_port: 1 + + # Remote Site Switch (Port 2) --> Remote Site PC 1 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-PC-1 + endpoint_b_port: 1 + + # Remote Site Switch (Port 3) --> Remote Site PC 2 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-PC-2 + endpoint_b_port: 1 + + # Remote Site Switch (Port 4) --> Remote Site Server (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: REM-PUB-SRV + endpoint_b_port: 1 + + ####################### + # SOME_TECH DMZ Links # + ####################### + + # Internet Router (Port 4) --> Some Tech DMZ Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 4 + endpoint_b_hostname: ST-PUB-FW + endpoint_b_port: 1 + + # Some Tech DMZ Firewall (DMZ Port) --> Some Tech Web Server (Port 1) + - endpoint_a_hostname: ST-PUB-FW + endpoint_a_port: 3 + endpoint_b_hostname: ST-DMZ-PUB-SRV-WEB + endpoint_b_port: 1 + + ############################ + # SOME_TECH INTRANET Links # + ############################ + + # Some Tech Intranet CR Router (Port 1) --> Some Tech DMZ Firewall (Internal Port) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 1 + endpoint_b_hostname: ST-PUB-FW + endpoint_b_port: 2 + + # Some Tech Intranet CR Router (Port 2) --> Some Tech Intranet DR Router 1 (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 2 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_b_port: 1 + + # Some Tech Intranet CR Router (Port 3) --> Some Tech Intranet DR Router 2 (Port 2) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 3 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-2 + endpoint_b_port: 1 + + # Some Tech Intranet Private Router CR (Port 4) --> Some Tech Data Private Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-CR + endpoint_a_port: 4 + endpoint_b_hostname: ST-DATA-PRV-SW-AS + endpoint_b_port: 1 + + + ############################### + # SOME_TECH HEAD OFFICE Links # + ############################### + + # Some Tech Head Office Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 2) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-2 + endpoint_b_port: 2 + + # Some Tech Head Office Switch (Port 2) --> Some Tech Head Office PC 1 (Port 1) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-HO-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 3) --> Some Tech Head Office PC 2 (Port 1) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-HO-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 4) --> Some Tech Head Office PC 3 (Port 1) + - endpoint_a_hostname: ST-HO-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-HO-PRV-PC-3 + endpoint_b_port: 1 + + + ################################### + # SOME_TECH HUMAN RESOURCES Links # + ################################### + + # Some Tech Human Resources Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 3) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST-INTRA-PRV-RT-DR-2 + endpoint_b_port: 3 + + # Some Tech Human Resources Switch (Port 2) --> Some Tech Human Resources PC 1 (Port 1) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-HR-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 3) --> Some Tech Human Resources PC 2 (Port 1) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-HR-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 4) --> Some Tech Human Resources PC 3 (Port 1) + - endpoint_a_hostname: ST-HR-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-HR-PRV-PC-3 + endpoint_b_port: 1 + + ######################## + # SOME_TECH DATA Links # + ######################## + + # Some Tech Data Switch (Port 2) --> Some Tech Data Private Storage Server (Port 1) + - endpoint_a_hostname: ST-DATA-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-DATA-PRV-SRV-STORAGE + endpoint_b_port: 1 + + # Some Tech Data Switch (Port 3) --> Some Tech Data Private Database Server (Port 1) + + - endpoint_a_hostname: ST-DATA-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-DATA-PRV-SRV-DB + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT A Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 2) --> Some Tech Private Project A Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 2) --> Some Tech Project A PC 1 + - endpoint_a_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-A-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 3) --> Some Tech Project A PC 2 + - endpoint_a_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-A-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 4) --> Some Tech Project A PC 3 + - endpoint_a_hostname: ST-PROJ-A-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-A-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT B Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 3) --> Some Tech Private Project B Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 2) --> Some Tech Project B PC 1 + - endpoint_a_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-B-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 3) --> Some Tech Project B PC 2 + - endpoint_a_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-B-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 4) --> Some Tech Project B PC 3 + - endpoint_a_hostname: ST-PROJ-B-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-B-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT C Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 4) --> Some Tech Private Project C Switch (Port 1) + - endpoint_a_hostname: ST-INTRA-PRV-RT-DR-1 + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 2) --> Some Tech Project C PC 1 + - endpoint_a_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST-PROJ-C-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 3) --> Some Tech Project C PC 2 + - endpoint_a_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST-PROJ-C-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 4) --> Some Tech Project C PC 3 + - endpoint_a_hostname: ST-PROJ-C-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST-PROJ-C-PRV-PC-3 + endpoint_b_port: 1 + +################################## +# Use Case 7 Agent YAML Anchors # +################################## + +############################## +# Green Agent YAML Anchors # +############################## + +# Green Agent Reward Impacts Values # + +LOW_WEIGHT_IMPACT: &LOW_WEIGHT_IMPACT 0.2 +MEDIUM_WEIGHT_IMPACT: &MEDIUM_WEIGHT_IMPACT 0.5 +HIGH_WEIGHT_IMPACT: &HIGH_WEIGHT_IMPACT 0.95 + +LOW_WEIGHT_IMPACT_NEGATIVE: &LOW_WEIGHT_IMPACT_NEG -0.2 +MEDIUM_WEIGHT_IMPACT_NEGATIVE: &MEDIUM_WEIGHT_IMPACT_NEG -0.5 +HIGH_WEIGHT_IMPACT_NEGATIVE: &HIGH_WEIGHT_IMPACT_NEG -0.8 + +# Default Green Agent Action Space Configuration Anchor # + +DEFAULT_GREEN_AGENT_MAX_EXECUTIONS: &DEFAULT_GREEN_AGENT_MAX_EXECUTIONS 1000 # Ensures green agent activity through-out an episode + +################################################# +# Probabilistic Green Agent Config Yaml Anchors # +################################################# + +# Probabilistic Green Agent | 20% NODE_APPLICATION_EXECUTE | 80% do-nothing # +PROBABILISTIC_CONFIG_20_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_20 + action_probabilities: + 0: 0.8 + 1: 0.2 + +# Probabilistic Green Agent | 40% NODE_APPLICATION_EXECUTE | 60% do-nothing # +PROBABILISTIC_CONFIG_40_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_40 + action_probabilities: + 0: 0.6 + 1: 0.4 + +# Probabilistic Green Agent | 60% NODE_APPLICATION_EXECUTE | 40% do-nothing # +PROBABILISTIC_CONFIG_20_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_60 + action_probabilities: + 0: 0.4 + 1: 0.6 + + +# System Green Agent Config UC7 Network Wide Yaml Anchor # + +# Lists the IP_Address of all hosts that contain DNS and NTP Service Clients +UC7_IP_LIST: &UC7_IP_LIST + # ====== Home Office ======== + - 192.168.1.2 # HOME-PUB-PC-1 | ip_id: 2 + - 192.168.1.3 # HOME-PUB-PC-2 | ip_id: 3 + - 192.168.1.4 # HOME-PUB-PC-SRV | ip_id: 4 + # ====== Remote Site ======== + - 192.168.20.2 # REM-PUB-PC-1 | ip_id: 5 + - 192.168.20.3 # REM-PUB-PC-2 | ip_id: 6 + - 192.168.20.4 # REM-PUB-SRV | ip_id: 7 + # ====== ST Public DMZ ======= + - *ST_PUB_SRV_WEB_IP # 192.168.100.2 (ST-DMZ-PUB-SRV-WEB) | ip_id: 8 + # ====== ST Head Office ======= + - 192.168.200.2 # ST-HO-PRV-PC-1 | ip_id: 9 + - 192.168.200.3 # ST-HO-PRV-PC-2 | ip_id: 10 + - 192.168.200.4 # ST-HO-PRV-PC-3 | ip_id: 11 + # ===== ST Human Resources ====== + - 192.168.210.2 # ST-HR-PRV-PC-1 | ip_id: 12 + - 192.168.210.3 # ST-HR-PRV-PC-2 | ip_id: 13 + - 192.168.210.4 # ST-HR-PRV-PC-3 | ip_id: 14 + # ====== ST DATA Servers ======= + - *ST_SRV_STORAGE_IP # 192.168.220.2 (ST-DATA-PRV-SRV-STORAGE) | ip_id: 15 + - *ST_SRV_DB_IP # 192.168.220.3 (ST-DATA-PRV-SRV-DB) | ip_id: 16 + # ====== ST Project A ======= + - 192.168.230.2 # PROJ-A-PRV-PC-1 | ip_id: 17 + - 192.168.230.3 # PROJ-A-PRV-PC-2 | ip_id: 18 + - 192.168.230.4 # PROJ-A-PRV-PC-3 | ip_id: 19 + # ====== ST Project B ======= + - 192.168.240.2 # PROJ-B-PRV-PC-1 | ip_id: 20 + - 192.168.240.3 # PROJ-B-PRV-PC-2 | ip_id: 21 + - 192.168.240.4 # PROJ-B-PRV-PC-3 | ip_id: 22 + # ====== ST Project C ======= + - 192.168.250.2 # PROJ-C-PRV-PC-1 | ip_id: 23 + - 192.168.250.3 # PROJ-C-PRV-PC-2 | ip_id: 24 + - 192.168.250.4 # PROJ-C-PRV-PC-3 | ip_id: 25 + +############################################ +# Use Case 7 Agent Configuration Section # +############################################ + +agents: + ####################################################### + # UC7 Green Agents Path of Life (POL) Configuration # + ####################################################### + + + #################################### + # Home Office Network POL Config # + #################################### + + # Home Office Green Agent Pattern Of Life + # ====================================== + # 1. Three Home workers accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. Three Home workers accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: HOME_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-1"] + target_application: "database-client" + start_step: 4 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-2"] + target_application: "database-client" + start_step: 8 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + - ref: HOME_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + #################################### + # Remote Site Network POL Config # + #################################### + + # Remote Site Green Agent Pattern Of Life + # ====================================== + # 1. Three Remote workers accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. Three Remote workers accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: REMOTE_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-1"] + target_application: "database-client" + start_step: 12 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-2"] + target_application: "database-client" + start_step: 16 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + - ref: REMOTE_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + #################################### + # ST Project A Network POL Config # + #################################### + + # ST Project A Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + + - ref: PROJ_A-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-A-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-1 + + - ref: PROJ_A-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-1 + + - ref: PROJ_A-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-A-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-A-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-A-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-3 + + - ref: PROJ_A-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-A-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-A-PRV-PC-3 + + #################################### + # ST Project B Network POL Config # + #################################### + + # ST Project B Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: PROJ_B-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-B-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-1 + + - ref: PROJ_B-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-B-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-1 + + - ref: PROJ_B-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-B-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-B-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-3 + + - ref: PROJ_B-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-B-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-B-PRV-PC-3 + + #################################### + # ST Project C Network POL Config # + #################################### + + # ST Project C Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST-DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: PROJ_C-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-C-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-1 + + - ref: PROJ_C-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-C-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-1 + + - ref: PROJ_C-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-C-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-C-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + observation_space: {} + agent_settings: + possible_start_nodes: ["ST-PROJ-C-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-3 + + - ref: PROJ_C-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST-PROJ-C-PRV-PC-3 + + ###################################### + # ST Head Office Network POL Config # + ###################################### + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. The ST CEO accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 2. The ST CTO accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 3. The ST CFO accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: CEO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HO-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST-HO-PRV-PC-1 + + - ref: CTO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HO-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HO-PRV-PC-2 + + - ref: CFO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HO-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HO-PRV-PC-3 + + ########################################## + # ST Human Resources Network POL Config # + ########################################## + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. A senior HR staff accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + # 2. Two junior HR staff accessing the SOME_TECH Public web-server (ST-DMZ-PUB-SRV-WEB) + + - ref: SENIOR_HR + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HR-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HR-PRV-PC-1 + + - ref: JUNIOR_HR-1 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HR-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HR-PRV-PC-2 + + - ref: JUNIOR_HR-2 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST-HR-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST-HR-PRV-PC-3 + + ########################## + # UC7 Red Agent Config # + ########################## + + - *red +########################### +# UC7 Blue Agent Config # +########################### + + - ref: defender + team: BLUE + type: proxy-agent + observation_space: + type: custom + options: + components: + - type: nodes + label: NODES + options: + hosts: + # TAP001 Potential Starting Note | ST-PROJ-A-PRV-PC-1 + - hostname: ST-PROJ-A-PRV-PC-1 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST-PROJ-B-PRV-PC-2 + - hostname: ST-PROJ-B-PRV-PC-2 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST-PROJ-C-PRV-PC-3 + - hostname: ST-PROJ-C-PRV-PC-3 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # ST DATA Server Database + - hostname: ST-DATA-PRV-SRV-DB + folders: + - folder_name: database + files: + - file_name: database.db + routers: + - hostname: ST-INTRA-PRV-RT-CR # TAP003 Malicious ACL Target | ROUTER0 + - hostname: ST-INTRA-PRV-RT-DR-1 # TAP003 Malicious ACL Target | ROUTER1 + - hostname: REM-PUB-RT-DR # TAP003 Malicious ACL Target | ROUTER2 + num_ports: 5 + num_services: 2 + num_applications: 2 + num_folders: 2 + num_files: 1 + num_nics: 1 + ip_list: + # ip_list is indexed at 2: + # 0 reserved for padding to align with observations + # 1 reserved for ALL ips + *UC7_IP_LIST + wildcard_list: + - 0.0.0.1 # 0 + - 0.0.0.255 # 1 + - 0.0.255.255 # 2 + port_list: + # 0 is a padding placeholder + # 1 means ALL ports + - FTP # 2 + - DNS # 3 + - HTTP # 4 + - NTP # 5 + - POSTGRES_SERVER # 6 + - SSH # 7 + + protocol_list: + # 0 is padding placeholder + # 1 means ALL protocols + - ICMP # 2 + - TCP # 3 + - UDP # 4 + num_rules: 5 + include_num_access: true + include_nmne: true + monitored_traffic: + tcp: + - HTTP + - POSTGRES_SERVER + icmp: + - NONE + - type: links + label: LINKS + options: + link_references: + # HOME OFFICE SUBNET LINKS + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-RT-DR:eth-1 # 1 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-1:eth-1 # 2 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-2:eth-1 # 3 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-SRV:eth-1 # 4 + # Internet LINKS + - ISP-PUB-RT-BR:eth-1<->HOME-PUB-RT-DR:eth-2 # 5 + - ISP-PUB-RT-BR:eth-2<->ISP-PUB-SRV-DNS:eth-1 # 6 + - ISP-PUB-RT-BR:eth-3<->REM-PUB-FW:eth-1 # 7 + # Remote DMZ Links + - REM-PUB-FW:eth-2<->REM-PUB-RT-DR:eth-1 # 8 + # Remote Site Links + - REM-PUB-RT-DR:eth-2<->REM-PUB-SW-AS:eth-1 # 9 + - REM-PUB-SW-AS:eth-2<->REM-PUB-PC-1:eth-1 # 10 + - REM-PUB-SW-AS:eth-3<->REM-PUB-PC-2:eth-1 # 11 + - REM-PUB-SW-AS:eth-4<->REM-PUB-SRV:eth-1 # 12 + # SOME_TECH DMZ + - ISP-PUB-RT-BR:eth-4<->ST-PUB-FW:eth-1 # 13 + - ST-PUB-FW:eth-3<->ST-DMZ-PUB-SRV-WEB:eth-1 # 14 + # SOME_TECH Intranet + - ST-INTRA-PRV-RT-CR:eth-1<->ST-PUB-FW:eth-2 # 15 + - ST-INTRA-PRV-RT-CR:eth-2<->ST-INTRA-PRV-RT-DR-1:eth-1 # 16 + - ST-INTRA-PRV-RT-CR:eth-3<->ST-INTRA-PRV-RT-DR-2:eth-1 # 17 + - ST-INTRA-PRV-RT-CR:eth-4<->ST-DATA-PRV-SW-AS:eth-1 # 18 + # SOME_TECH Head Office + - ST-HO-PRV-SW-AS:eth-1<->ST-INTRA-PRV-RT-DR-2:eth-2 # 19 + - ST-HO-PRV-SW-AS:eth-2<->ST-HO-PRV-PC-1:eth-1 # 20 + - ST-HO-PRV-SW-AS:eth-3<->ST-HO-PRV-PC-2:eth-1 # 21 + - ST-HO-PRV-SW-AS:eth-4<->ST-HO-PRV-PC-3:eth-1 # 22 + # SOME_TECH Human Resources + - ST-HR-PRV-SW-AS:eth-1<->ST-INTRA-PRV-RT-DR-2:eth-3 # 23 + - ST-HR-PRV-SW-AS:eth-2<->ST-HR-PRV-PC-1:eth-1 # 24 + - ST-HR-PRV-SW-AS:eth-3<->ST-HR-PRV-PC-2:eth-1 # 25 + - ST-HR-PRV-SW-AS:eth-4<->ST-HR-PRV-PC-3:eth-1 # 26 + # SOME_TECH Data Links + - ST-DATA-PRV-SW-AS:eth-2<->ST-DATA-PRV-SRV-STORAGE:eth-1 # 27 + - ST-DATA-PRV-SW-AS:eth-3<->ST-DATA-PRV-SRV-DB:eth-1 # 28 + # SOME_TECH Project A Links + - ST-INTRA-PRV-RT-DR-1:eth-2<->ST-PROJ-A-PRV-SW-AS:eth-1 # 29 + - ST-PROJ-A-PRV-SW-AS:eth2<->ST-PROJ-A-PRV-PC-1:eth-1 # 31 + - ST-PROJ-A-PRV-SW-AS:eth3<->ST-PROJ-A-PRV-PC-2:eth-1 # 32 + - ST-PROJ-A-PRV-SW-AS:eth4<->ST-PROJ-A-PRV-PC-3:eth-1 # 33 + # SOME_TECH Project B Links + - ST-INTRA-PRV-RT-DR-1:eth-3<->ST-PROJ-B-PRV-SW-AS:eth-1 # 34 + - ST-PROJ-B-PRV-SW-AS:eth2<->ST-PROJ-B-PRV-PC-1:eth-1 # 35 + - ST-PROJ-B-PRV-SW-AS:eth3<->ST-PROJ-B-PRV-PC-2:eth-1 # 36 + - ST-PROJ-B-PRV-SW-AS:eth4<->ST-PROJ-B-PRV-PC-3:eth-1 # 37 + # SOME_TECH Project C Links + - ST-INTRA-PRV-RT-DR-1:eth-4<->ST-PROJ-C-PRV-SW-AS:eth-1 # 38 + - ST-PROJ-A-PRV-SW-AS:eth2<->ST-PROJ-C-PRV-PC-1:eth-1 # 39 + - ST-PROJ-A-PRV-SW-AS:eth3<->ST-PROJ-C-PRV-PC-2:eth-1 # 40 + - ST-PROJ-A-PRV-SW-AS:eth4<->ST-PROJ-C-PRV-PC-3:eth-1 # 41 + action_space: + action_map: + 0: + action: do-nothing + options: {} + + # |======================================| + # | ST-PROJ-A-PRV-PC-1 | + # |======================================| + + # ST-PROJ-A-PRV-PC-1 | node-os-scan + 1: + action: node-os-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | node-shutdown + 2: + action: node-shutdown + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | node-startup + 3: + action: node-startup + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | node-reset + 4: + action: node-reset + options: + node_name: ST-PROJ-A-PRV-PC-1 + # ST-PROJ-A-PRV-PC-1 | host-nic-disable + 5: + action: host-nic-disable + options: + node_name: ST-PROJ-A-PRV-PC-1 + nic_num: 0 + # ST-PROJ-A-PRV-PC-1 | host-nic-enable + 6: + action: host-nic-enable + options: + node_name: ST-PROJ-A-PRV-PC-1 + nic_num: 0 + # ST-PROJ-A-PRV-PC-1 | node-application-close | database-client + 7: + action: node-application-close + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + # ST-PROJ-A-PRV-PC-1 | node-application-scan | database-client + 8: + action: node-application-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + # ST-PROJ-A-PRV-PC-1 | node-application-fix | database-client + 9: + action: node-application-fix + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + # ST-PROJ-A-PRV-PC-1 | node-application-remove | database-client + 10: + action: node-application-remove + options: + node_name: ST-PROJ-A-PRV-PC-1 + application_name: database-client + + + # ST-PROJ-A-PRV-PC-1 | node-file-scan | downloads/malware_dropper.ps1 + 11: + action: node-file-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST-PROJ-A-PRV-PC-1 | node-file-scan | exfiltration_folder/database.db + 12: + action: node-file-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + file_name: database.db + + # ST-PROJ-A-PRV-PC-1 | node-folder-scan | downloads/ + 13: + action: node-folder-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: downloads + + # ST-PROJ-A-PRV-PC-1 | node-folder-scan | exfiltration_folder/ + 14: + action: node-folder-scan + options: + node_name: ST-PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + + # |======================================| + # | ST-PROJ-B-PRV-PC-2 | + # |======================================| + + # ST-PROJ-B-PRV-PC-2 | node-os-scan + 15: + action: node-os-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | node-shutdown + 16: + action: node-shutdown + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | node-startup + 17: + action: node-startup + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | node-reset + 18: + action: node-reset + options: + node_name: ST-PROJ-B-PRV-PC-2 + + # ST-PROJ-B-PRV-PC-2 | host-nic-disable + 19: + action: host-nic-disable + options: + node_name: ST-PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST-PROJ-B-PRV-PC-2 | host-nic-enable + 20: + action: host-nic-enable + options: + node_name: ST-PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST-PROJ-B-PRV-PC-2 | node-application-close | database-client + 21: + action: node-application-close + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-application-scan | database-client + 22: + action: node-application-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-application-fix | database-client + 23: + action: node-application-fix + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-application-remove | database-client + 24: + action: node-application-remove + options: + node_name: ST-PROJ-B-PRV-PC-2 + application_name: database-client + + # ST-PROJ-B-PRV-PC-2 | node-file-scan | downloads/malware_dropper.ps1 + 25: + action: node-file-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST-PROJ-B-PRV-PC-2 | node-file-scan | exfiltration_folder/database.db + 26: + action: node-file-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + file_name: database.db + + # ST-PROJ-B-PRV-PC-2 | node-folder-scan | downloads/ + 27: + action: node-folder-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: downloads + + # ST-PROJ-B-PRV-PC-2 | node-folder-scan | exfiltration_folder/ + 28: + action: node-folder-scan + options: + node_name: ST-PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + + # |======================================| + # | ST-PROJ-C-PRV-PC-3 | + # |======================================| + + # ST-PROJ-C-PRV-PC-3 | node-os-scan + 29: + action: node-os-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | node-shutdown + 30: + action: node-shutdown + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | node-startup + 31: + action: node-startup + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | node-reset + 32: + action: node-reset + options: + node_name: ST-PROJ-C-PRV-PC-3 + + # ST-PROJ-C-PRV-PC-3 | host-nic-disable + 33: + action: host-nic-disable + options: + node_name: ST-PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST-PROJ-C-PRV-PC-3 | host-nic-enable + 34: + action: host-nic-enable + options: + node_name: ST-PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST-PROJ-C-PRV-PC-3 | node-application-close | database-client + 35: + action: node-application-close + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-application-scan | database-client + 36: + action: node-application-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-application-fix | database-client + 37: + action: node-application-fix + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-application-remove | database-client + 38: + action: node-application-remove + options: + node_name: ST-PROJ-C-PRV-PC-3 + application_name: database-client + + # ST-PROJ-C-PRV-PC-3 | node-file-scan | downloads/malware_dropper.ps1 + 39: + action: node-file-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST-PROJ-C-PRV-PC-3 | node-file-scan | exfiltration_folder/database.db + 40: + action: node-file-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + file_name: database.db + + # ST-PROJ-C-PRV-PC-3 | node-folder-scan | downloads/ + 41: + action: node-folder-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: downloads + + # ST-PROJ-C-PRV-PC-3 | node-folder-scan | exfiltration_folder/ + 42: + action: node-folder-scan + options: + node_name: ST-PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + + # |======================================| + # | ST-INTRA-PRV-RT-CR | + # |======================================| + + # ST-INTRA-PRV-RT-CR | router-acl-add-rule | P2: ST-PROJ-A-PRV-PC-1 !==> ST-DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 43: + action: router-acl-add-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST-PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST-DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST-INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 1 + 44: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 1 + + # ST-INTRA-PRV-RT-CR | router-acl-add-rule | P3: ST-PROJ-B-PRV-PC-2 !==> ST-DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 45: + action: router-acl-add-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 2 + permission: DENY + src_ip: 192.168.240.3 # (ST-PROJ-B-PRV-PC-2) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST-DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + # ST-INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 2 + 46: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 2 + + # ST-INTRA-PRV-RT-CR | router-acl-add-rule | P4: ST-PROJ-C-PRV-PC-3 !==> ST-DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 47: + action: router-acl-add-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 3 + permission: DENY + src_ip: 192.168.250.4 # (ST-PROJ-C-PRV-PC-3) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST-DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST-INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 3 + 48: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-CR + position: 3 + + # |======================================| + # | ST-DATA-PRV-SRV-DB | + # |======================================| + + # ST-DATA-PRV-SRV-DB | node-file-scan | Scans the database.db file (health status) + 49: + action: node-file-scan + options: + node_name: ST-DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # ST-DATA-PRV-SRV-DB | node-account-change-password | Changes the password of a user account + 50: + action: node-account-change-password + options: + node_name: ST-DATA-PRV-SRV-DB + username: admin # default account + current_password: admin # default password + new_password: thr33_alert_wolv3z # A more 'secure' password + + # |======================================| + # | ST-INTRA-PRV-RT-DR-1 | + # |======================================| + + # ST-INTRA-PRV-RT-DR-1 | router-acl-add-rule | P1: ST-INTRA-PRV-RT-DR-1 !==> ANY (TCP:SSH) + 51: + action: router-acl-add-rule + options: + target_router: ST-INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST-PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: SSH + dst_ip: ALL + dst_wildcard: 0.0.255.255 + dst_port: SSH + protocol_name: TCP + + # ST-INTRA-PRV-RT-DR-1 | node-account-change-password + 52: + action: node-account-change-password + options: + node_name: ST-INTRA-PRV-RT-DR-1 + username: admin + current_password: admin + new_password: secure_password + + # ST-INTRA-PRV-RT-DR-1 | router-acl-remove-rule | Removes the given ACL at position 1 + 53: + action: router-acl-remove-rule + options: + target_router: ST-INTRA-PRV-RT-DR-1 + position: 1 + + # |======================================| + # | REM-PUB-RT-DR | + # |======================================| + + # REM-PUB-RT-DR | node-account-change-password + 54: + action: node-account-change-password + options: + node_name: REM-PUB-RT-DR + username: admin + current_password: admin + new_password: secure_password + + # REM-PUB-RT-DR | router-acl-remove-rule | Removes the given ACL at position 1 + 55: + action: router-acl-remove-rule + options: + target_router: REM-PUB-RT-DR + position: 1 + + reward_function: + reward_components: + - type: database-file-integrity + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST-DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # Home Site Green Agents (32 Green Agents each contributing 0.03125 of blue reward) + + # Blue Shared Reward | HOME_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-DB + + # Blue Shared Reward | HOME_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-WEB + + # Blue Shared Reward | HOME_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-DB + + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-WEB + + # Remote Site Green Agents + + # Blue Shared Reward | REMOTE_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-DB + + # Blue Shared Reward | REMOTE_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-WEB + + # Blue Shared Reward | REMOTE_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-DB + + # Blue Shared Reward | REMOTE_WORKER-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-WEB + + # ST Project A Green Agents + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-WEB + + # ST Project B Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Project C Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Head Office Green Agents (CEO/CFO/CTO) + + # Blue Shared Reward | CEO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CEO + + # Blue Shared Reward | CFO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CFO + + # Blue Shared Reward | CTO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CTO + + # ST Human Resources Green Agents + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: SENIOR_HR + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-1 + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-2 + + agent_settings: + flatten_obs: False + + + + # ####################################### + # # UC7 Network Wide System POL Config # + # ####################################### + + # # UC7 Network Wide System Pattern Of Life + # # ========================================== + # # 1. All NTP Clients perform periodic NTP Server transactions. + # # 2. Periodic Database (ST-DATA-PRV-SRV-DB) FTP backups (ST-DATA-PRV-SRV-STORAGE) + # # 3. All DNS Clients perform periodic DNS Server transactions. + + # # TODO: NTPClient Node Service Start doesn't start a 'Request' + + # # 1. All NTP Clients perform periodic NTP Server transactions. + + # - ref: SYSTEM-NTP + # team: GREEN + # type: probabilistic-agent + # observation_space: null + # action_space: + # action_list: + # - type: do-nothing + # - type: NODE_SERVICE_START + # action_map: + # # ====== Home Office ======== + # 0: + # action: do-nothing + # options: {} + # 1: + # action: NODE_SERVICE_START + # options: + # node_id: 0 # HOME-PUB-PC-1 + # service_id: 0 # NTPClient + # 2: + # action: NODE_SERVICE_START + # options: + # node_id: 1 # HOME-PUB-PC-2 + # service_id: 0 + # 3: + # action: NODE_SERVICE_START + # options: + # node_id: 2 # HOME-PUB-PC-SRV + # service_id: 0 + # # ====== Remote Site ======== + # 4: + # action: NODE_SERVICE_START + # options: + # node_id: 3 # REM-PUB-PC-1 + # service_id: 0 + # 5: + # action: NODE_SERVICE_START + # options: + # node_id: 4 # REM-PUB-PC-2 + # service_id: 0 + # 6: + # action: NODE_SERVICE_START + # options: + # node_id: 5 # REM-PUB-SRV + # service_id: 0 + # # ====== ST Public DMZ ======= + # 7: + # action: NODE_SERVICE_START + # options: + # node_id: 6 # ST-DMZ-PUB-SRV-WEB + # service_id: 0 + # # ====== ST Head Office ======= + # 8: + # action: NODE_SERVICE_START + # options: + # node_id: 7 # ST-HO-PRV-PC-1 + # service_id: 0 + # 9: + # action: NODE_SERVICE_START + # options: + # node_id: 8 # ST-HO-PRV-PC-2 + # service_id: 0 + # 10: + # action: NODE_SERVICE_START + # options: + # node_id: 9 # ST-HO-PRV-PC-3 + # service_id: 0 + # # ====== ST Human Resources ======= + # 11: + # action: NODE_SERVICE_START + # options: + # node_id: 10 # ST-HR-PRV-PC-1 + # service_id: 0 + # 12: + # action: NODE_SERVICE_START + # options: + # node_id: 11 # ST-HR-PRV-PC-2 + # service_id: 0 + # 13: + # action: NODE_SERVICE_START + # options: + # node_id: 12 # ST-HR-PRV-PC-3 + # service_id: 0 + # # ====== ST DATA Servers ======= + # 14: + # action: NODE_SERVICE_START + # options: + # node_id: 13 # ST-DATA-PRV-SRV-STORAGE + # service_id: 0 + # 15: + # action: NODE_SERVICE_START + # options: + # node_id: 14 # ST-DATA-PRV-SRV-DB + # service_id: 0 + # # ====== ST Project A ======= + # 16: + # action: NODE_SERVICE_START + # options: + # node_id: 15 # PROJ-A-PRV-PC-1 + # service_id: 0 + # 17: + # action: NODE_SERVICE_START + # options: + # node_id: 16 # PROJ-A-PRV-PC-2 + # service_id: 0 + # 18: + # action: NODE_SERVICE_START + # options: + # node_id: 17 # PROJ-A-PRV-PC-3 + # service_id: 0 + # # ====== ST Project B ======= + # 19: + # action: NODE_SERVICE_START + # options: + # node_id: 18 # PROJ-B-PRV-PC-1 + # service_id: 0 + # 20: + # action: NODE_SERVICE_START + # options: + # node_id: 19 # PROJ-B-PRV-PC-2 + # service_id: 0 + # 21: + # action: NODE_SERVICE_START + # options: + # node_id: 20 # PROJ-B-PRV-PC-3 + # service_id: 0 + # # ====== ST Project C ======= + # 22: + # action: NODE_SERVICE_START + # options: + # node_id: 21 # PROJ-C-PRV-PC-1 + # service_id: 0 + # 23: + # action: NODE_SERVICE_START + # options: + # node_id: 22 # PROJ-C-PRV-PC-2 + # service_id: 0 + # 24: + # action: NODE_SERVICE_START + # options: + # node_id: 23 # PROJ-C-PRV-PC-3 + # service_id: 0 + # options: + # nodes: + # # ====== Home Office ======== + # - node_name: HOME-PUB-PC-1 + # services: + # - service_name: NTPClient + # - node_name: HOME-PUB-PC-2 + # services: + # - service_name: NTPClient + # - node_name: HOME-PUB-SRV + # services: + # - service_name: NTPClient + # # ====== Remote Site ======== + # - node_name: REM-PUB-PC-1 + # services: + # - service_name: NTPClient + # - node_name: REM-PUB-PC-2 + # services: + # - service_name: NTPClient + # - node_name: REM-PUB-SRV + # services: + # - service_name: NTPClient + # # ====== ST Public DMZ ======= + # - node_name: ST-DMZ-PUB-SRV-WEB + # services: + # - service_name: NTPClient + # # ====== ST Head Office ======= + # - node_name: ST-HO-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: ST-HO-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: ST-HO-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ===== ST Human Resources ====== + # - node_name: ST-HR-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: ST-HR-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: ST-HR-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ====== ST DATA Servers ======= + # - node_name: ST-DATA-PRV-SRV-STORAGE + # services: + # - service_name: NTPClient + # - node_name: ST-DATA-PRV-SRV-DB + # services: + # - service_name: NTPClient + # # ====== ST Project A ======= + # - node_name: PROJ-A-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: PROJ-A-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: PROJ-A-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ====== ST Project B ======= + # - node_name: PROJ-B-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: PROJ-B-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: PROJ-B-PRV-PC-3 + # services: + # - service_name: NTPClient + # # ====== ST Project C ======= + # - node_name: PROJ-C-PRV-PC-1 + # services: + # - service_name: NTPClient + # - node_name: PROJ-C-PRV-PC-2 + # services: + # - service_name: NTPClient + # - node_name: PROJ-C-PRV-PC-3 + # services: + # - service_name: NTPClient + # ip_list: + # *UC7_IP_LIST + # max_folders_per_node: 1 + # max_files_per_folder: 1 + # max_services_per_node: 1 + # max_applications_per_node: 1 + # settings: + # start_step: 1 + # start_variance: 0 + # max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + # frequency: 1 + # variance: 0 + # reward_function: + # reward_components: + # - type: DUMMY + + # # 2. Periodic Database (ST-DATA-PRV-SRV-DB) FTP backups (ST-DATA-PRV-SRV-STORAGE) + + # - ref: SYSTEM-FTP + # team: GREEN + # type: probabilistic-agent + # observation_space: null + # action_space: + # action_list: + # - type: do-nothing + # - type: NODE_SERVICE_FIX + # action_map: + # 0: + # action: do-nothing + # options: {} + # 1: + # action: NODE_SERVICE_FIX + # options: + # node_id: 0 + # service_id: 0 + # options: + # nodes: + # - node_name: ST-DATA-PRV-SRV-DB + # applications: + # - application_name: database-client + # ip_list: + # - *ST_SRV_DB_IP + # max_folders_per_node: 1 + # max_files_per_folder: 1 + # max_services_per_node: 1 + # max_applications_per_node: 1 + # settings: + # start_step: 4 + # start_variance: 0 + # max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + # frequency: 4 + # variance: 0 + # reward_function: + # reward_components: + # - type: DUMMY + + # # 3. All DNS Clients perform periodic DNS Server transactions. + + # # TODO: Address issue where dns-clients can't be started directly via agents + # - ref: SYSTEM-DNS + # team: GREEN + # type: probabilistic-agent + # observation_space: null + # action_space: + # action_list: + # - type: do-nothing + # - type: NODE_SERVICE_START + # action_map: + # # ====== Home Office ======== + # 0: + # action: do-nothing + # options: {} + # 1: + # action: NODE_SERVICE_START + # options: + # node_id: 0 # HOME-PUB-PC-1 + # service_id: 0 # dns-client + # 2: + # action: NODE_SERVICE_START + # options: + # node_id: 1 # HOME-PUB-PC-2 + # service_id: 0 + # 3: + # action: NODE_SERVICE_START + # options: + # node_id: 2 # HOME-PUB-PC-SRV + # service_id: 0 + # # ====== Remote Site ======== + # 4: + # action: NODE_SERVICE_START + # options: + # node_id: 3 # REM-PUB-PC-1 + # service_id: 0 + # 5: + # action: NODE_SERVICE_START + # options: + # node_id: 4 # REM-PUB-PC-2 + # service_id: 0 + # 6: + # action: NODE_SERVICE_START + # options: + # node_id: 5 # REM-PUB-SRV + # service_id: 0 + # # ====== ST Public DMZ ======= + # 7: + # action: NODE_SERVICE_START + # options: + # node_id: 6 # ST-DMZ-PUB-SRV-WEB + # service_id: 0 + # # ====== ST Head Office ======= + # 8: + # action: NODE_SERVICE_START + # options: + # node_id: 7 # ST-HO-PRV-PC-1 + # service_id: 0 + # 9: + # action: NODE_SERVICE_START + # options: + # node_id: 8 # ST-HO-PRV-PC-2 + # service_id: 0 + # 10: + # action: NODE_SERVICE_START + # options: + # node_id: 9 # ST-HO-PRV-PC-3 + # service_id: 0 + # # ====== ST Human Resources ======= + # 11: + # action: NODE_SERVICE_START + # options: + # node_id: 10 # ST-HR-PRV-PC-1 + # service_id: 0 + # 12: + # action: NODE_SERVICE_START + # options: + # node_id: 11 # ST-HR-PRV-PC-2 + # service_id: 0 + # 13: + # action: NODE_SERVICE_START + # options: + # node_id: 12 # ST-HR-PRV-PC-3 + # service_id: 0 + # # ====== ST DATA Servers ======= + # 14: + # action: NODE_SERVICE_START + # options: + # node_id: 13 # ST-DATA-PRV-SRV-STORAGE + # service_id: 0 + # 15: + # action: NODE_SERVICE_START + # options: + # node_id: 14 # ST-DATA-PRV-SRV-DB + # service_id: 0 + # # ====== ST Project A ======= + # 16: + # action: NODE_SERVICE_START + # options: + # node_id: 15 # PROJ-A-PRV-PC-1 + # service_id: 0 + # 17: + # action: NODE_SERVICE_START + # options: + # node_id: 16 # PROJ-A-PRV-PC-2 + # service_id: 0 + # 18: + # action: NODE_SERVICE_START + # options: + # node_id: 17 # PROJ-A-PRV-PC-3 + # service_id: 0 + # # ====== ST Project B ======= + # 19: + # action: NODE_SERVICE_START + # options: + # node_id: 18 # PROJ-B-PRV-PC-1 + # service_id: 0 + # 20: + # action: NODE_SERVICE_START + # options: + # node_id: 19 # PROJ-B-PRV-PC-2 + # service_id: 0 + # 21: + # action: NODE_SERVICE_START + # options: + # node_id: 20 # PROJ-B-PRV-PC-3 + # service_id: 0 + # # ====== ST Project C ======= + # 22: + # action: NODE_SERVICE_START + # options: + # node_id: 21 # PROJ-C-PRV-PC-1 + # service_id: 0 + # 23: + # action: NODE_SERVICE_START + # options: + # node_id: 22 # PROJ-C-PRV-PC-2 + # service_id: 0 + # 24: + # action: NODE_SERVICE_START + # options: + # node_id: 23 # PROJ-C-PRV-PC-3 + # service_id: 0 + # options: + # nodes: + # # ====== Home Office ======== + # - node_name: HOME-PUB-PC-1 + # services: + # - service_name: dns-client + # - node_name: HOME-PUB-PC-2 + # services: + # - service_name: dns-client + # - node_name: HOME-PUB-SRV + # services: + # - service_name: dns-client + # # ====== Remote Site ======== + # - node_name: REM-PUB-PC-1 + # services: + # - service_name: dns-client + # - node_name: REM-PUB-PC-2 + # services: + # - service_name: dns-client + # - node_name: REM-PUB-SRV + # services: + # - service_name: dns-client + # # ====== ST Public DMZ ======= + # - node_name: ST-DMZ-PUB-SRV-WEB + # services: + # - service_name: dns-client + # # ====== ST Head Office ======= + # - node_name: ST-HO-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: ST-HO-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: ST-HO-PRV-PC-3 + # services: + # - service_name: dns-client + # # ===== ST Human Resources ====== + # - node_name: ST-HR-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: ST-HR-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: ST-HR-PRV-PC-3 + # services: + # - service_name: dns-client + # # ====== ST DATA Servers ======= + # - node_name: ST-DATA-PRV-SRV-STORAGE + # services: + # - service_name: dns-client + # - node_name: ST-DATA-PRV-SRV-DB + # services: + # - service_name: dns-client + # # ====== ST Project A ======= + # - node_name: PROJ-A-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: PROJ-A-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: PROJ-A-PRV-PC-3 + # services: + # - service_name: dns-client + # # ====== ST Project B ======= + # - node_name: PROJ-B-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: PROJ-B-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: PROJ-B-PRV-PC-3 + # services: + # - service_name: dns-client + # # ====== ST Project C ======= + # - node_name: PROJ-C-PRV-PC-1 + # services: + # - service_name: dns-client + # - node_name: PROJ-C-PRV-PC-2 + # services: + # - service_name: dns-client + # - node_name: PROJ-C-PRV-PC-3 + # services: + # - service_name: dns-client + # ip_list: + # *UC7_IP_LIST + # max_folders_per_node: 1 + # max_files_per_folder: 1 + # max_services_per_node: 1 + # max_applications_per_node: 1 + # settings: + # start_step: 1 + # start_variance: 0 + # max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + # frequency: 1 + # variance: 0 + # reward_function: + # reward_components: + # - type: DUMMY diff --git a/src/primaite/game/agent/actions/node.py b/src/primaite/game/agent/actions/node.py index 19639c21..b1b6ec12 100644 --- a/src/primaite/game/agent/actions/node.py +++ b/src/primaite/game/agent/actions/node.py @@ -1,6 +1,6 @@ # © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK from abc import ABC, abstractmethod -from typing import ClassVar, List, Optional, Union +from typing import ClassVar, List, Literal, Optional, Union from primaite.game.agent.actions.manager import AbstractAction from primaite.interface.request import RequestFormat @@ -153,8 +153,6 @@ class NodeNMAPPortScanAction(NodeNMAPAbstractAction, discriminator="node-nmap-po class NodeNetworkServiceReconAction(NodeNMAPAbstractAction, discriminator="node-network-service-recon"): """Action which performs an nmap network service recon (ping scan followed by port scan).""" - config: "NodeNetworkServiceReconAction.ConfigSchema" - class ConfigSchema(NodeNMAPAbstractAction.ConfigSchema): """Configuration schema for NodeNetworkServiceReconAction.""" @@ -179,3 +177,70 @@ class NodeNetworkServiceReconAction(NodeNMAPAbstractAction, discriminator="node- "show": config.show, }, ] + + +class NodeAccountsAddUserAction(AbstractAction, discriminator="node-account-add-user"): + class ConfigSchema(AbstractAction.ConfigSchema): + type: Literal["node-account-add-user"] = "node-account-add-user" + node_name: str + username: str + password: str + is_admin: bool + + @classmethod + @staticmethod + def form_request(config: ConfigSchema) -> RequestFormat: + return [ + "network", + "node", + config.node_name, + "service", + "user-manager", + "add_user", + config.username, + config.password, + config.is_admin, + ] + + +class NodeAccountsDisableUserAction(AbstractAction, discriminator="node-account-disable-user"): + class ConfigSchema(AbstractAction.ConfigSchema): + type: Literal["node-account-disable-user"] = "node-account-disable-user" + node_name: str + username: str + + @classmethod + @staticmethod + def form_request(config: ConfigSchema) -> RequestFormat: + return [ + "network", + "node", + config.node_name, + "service", + "user-manager", + "disable_user", + config.username, + ] + + +class NodeSendLocalCommandAction(AbstractAction, discriminator="node-send-local-command"): + class ConfigSchema(AbstractAction.ConfigSchema): + type: Literal["node-send-local-command"] = "node-send-local-command" + node_name: str + username: str + password: str + command: RequestFormat + + @staticmethod + def form_request(config: ConfigSchema) -> RequestFormat: + return [ + "network", + "node", + config.node_name, + "service", + "terminal", + "send_local_command", + config.username, + config.password, + {"command": config.command}, + ] diff --git a/src/primaite/game/agent/actions/session.py b/src/primaite/game/agent/actions/session.py index 58a8a555..63a45c5e 100644 --- a/src/primaite/game/agent/actions/session.py +++ b/src/primaite/game/agent/actions/session.py @@ -34,8 +34,6 @@ class NodeSessionAbstractAction(AbstractAction, ABC): class NodeSessionsRemoteLoginAction(NodeSessionAbstractAction, discriminator="node-session-remote-login"): """Action which performs a remote session login.""" - config: "NodeSessionsRemoteLoginAction.ConfigSchema" - class ConfigSchema(NodeSessionAbstractAction.ConfigSchema): """Configuration schema for NodeSessionsRemoteLoginAction.""" @@ -53,7 +51,7 @@ class NodeSessionsRemoteLoginAction(NodeSessionAbstractAction, discriminator="no config.node_name, "service", "terminal", - "node-session-remote-login", + "node_session_remote_login", config.username, config.password, config.remote_ip, @@ -63,8 +61,6 @@ class NodeSessionsRemoteLoginAction(NodeSessionAbstractAction, discriminator="no class NodeSessionsRemoteLogoutAction(NodeSessionAbstractAction, discriminator="node-session-remote-logoff"): """Action which performs a remote session logout.""" - config: "NodeSessionsRemoteLogoutAction.ConfigSchema" - class ConfigSchema(NodeSessionAbstractAction.ConfigSchema): """Configuration schema for NodeSessionsRemoteLogoutAction.""" @@ -78,14 +74,13 @@ class NodeSessionsRemoteLogoutAction(NodeSessionAbstractAction, discriminator="n return ["network", "node", config.node_name, "service", "terminal", config.verb, config.remote_ip] -class NodeAccountChangePasswordAction(NodeSessionAbstractAction, discriminator="node-account-change-password"): +class NodeAccountChangePasswordAction(AbstractAction, discriminator="node-account-change-password"): """Action which changes the password for a user.""" - config: "NodeAccountChangePasswordAction.ConfigSchema" - - class ConfigSchema(NodeSessionAbstractAction.ConfigSchema): + class ConfigSchema(AbstractAction.ConfigSchema): """Configuration schema for NodeAccountsChangePasswordAction.""" + node_name: str username: str current_password: str new_password: str diff --git a/src/primaite/game/agent/interface.py b/src/primaite/game/agent/interface.py index a55cd3ff..d06bd1d0 100644 --- a/src/primaite/game/agent/interface.py +++ b/src/primaite/game/agent/interface.py @@ -6,6 +6,7 @@ from abc import ABC, abstractmethod from typing import Any, ClassVar, Dict, List, Literal, Optional, Tuple, Type, TYPE_CHECKING from gymnasium.core import ActType, ObsType +from prettytable import PrettyTable from pydantic import BaseModel, ConfigDict, Field from primaite.game.agent.actions import ActionManager @@ -42,6 +43,9 @@ class AgentHistoryItem(BaseModel): reward_info: Dict[str, Any] = {} + observation: Optional[ObsType] = None + """The observation space data for this step.""" + class AbstractAgent(BaseModel, ABC): """Base class for scripted and RL agents.""" @@ -67,6 +71,9 @@ class AbstractAgent(BaseModel, ABC): default_factory=lambda: ObservationManager.ConfigSchema() ) reward_function: RewardFunction.ConfigSchema = Field(default_factory=lambda: RewardFunction.ConfigSchema()) + thresholds: Optional[Dict] = {} + # TODO: this is only relevant to some observations, need to refactor the way thresholds are dealt with (#3085) + """A dict containing the observation thresholds.""" config: ConfigSchema = Field(default_factory=lambda: AbstractAgent.ConfigSchema()) @@ -90,10 +97,42 @@ class AbstractAgent(BaseModel, ABC): def model_post_init(self, __context: Any) -> None: """Overwrite the default empty action, observation, and rewards with ones defined through the config.""" self.action_manager = ActionManager(config=self.config.action_space) + self.config.observation_space.options.thresholds = self.config.thresholds self.observation_manager = ObservationManager(config=self.config.observation_space) self.reward_function = RewardFunction(config=self.config.reward_function) return super().model_post_init(__context) + def add_agent_action(self, item: AgentHistoryItem, table: PrettyTable) -> PrettyTable: + """Update the given table with information from given AgentHistoryItem.""" + node, application = "unknown", "unknown" + if (node_id := item.parameters.get("node_id")) is not None: + node = self.action_manager.node_names[node_id] + if (application_id := item.parameters.get("application_id")) is not None: + application = self.action_manager.application_names[node_id][application_id] + if (application_name := item.parameters.get("application_name")) is not None: + application = application_name + table.add_row([item.timestep, item.action, node, application, item.response.status]) + return table + + def show_history(self, ignored_actions: Optional[list] = None): + """ + Print an agent action provided it's not the DONOTHING action. + + :param ignored_actions: OPTIONAL: List of actions to be ignored when displaying the history. + If not provided, defaults to ignore DONOTHING actions. + """ + if not ignored_actions: + ignored_actions = ["DONOTHING"] + table = PrettyTable() + table.field_names = ["Step", "Action", "Node", "Application", "Response"] + print(f"Actions for '{self.agent_name}':") + for item in self.history: + if item.action in ignored_actions: + pass + else: + table = self.add_agent_action(item=item, table=table) + print(table) + def update_observation(self, state: Dict) -> ObsType: """ Convert a state from the simulator into an observation for the agent using the observation space. @@ -140,12 +179,23 @@ class AbstractAgent(BaseModel, ABC): return request def process_action_response( - self, timestep: int, action: str, parameters: Dict[str, Any], request: RequestFormat, response: RequestResponse + self, + timestep: int, + action: str, + parameters: Dict[str, Any], + request: RequestFormat, + response: RequestResponse, + observation: ObsType, ) -> None: """Process the response from the most recent action.""" self.history.append( AgentHistoryItem( - timestep=timestep, action=action, parameters=parameters, request=request, response=response + timestep=timestep, + action=action, + parameters=parameters, + request=request, + response=response, + observation=observation, ) ) diff --git a/src/primaite/game/agent/observations/file_system_observations.py b/src/primaite/game/agent/observations/file_system_observations.py index ed9dcd8f..a9e3a9aa 100644 --- a/src/primaite/game/agent/observations/file_system_observations.py +++ b/src/primaite/game/agent/observations/file_system_observations.py @@ -26,7 +26,13 @@ class FileObservation(AbstractObservation, discriminator="file"): file_system_requires_scan: Optional[bool] = None """If True, the file must be scanned to update the health state. Tf False, the true state is always shown.""" - def __init__(self, where: WhereType, include_num_access: bool, file_system_requires_scan: bool) -> None: + def __init__( + self, + where: WhereType, + include_num_access: bool, + file_system_requires_scan: bool, + thresholds: Optional[Dict] = {}, + ) -> None: """ Initialise a file observation instance. @@ -48,10 +54,36 @@ class FileObservation(AbstractObservation, discriminator="file"): if self.include_num_access: self.default_observation["num_access"] = 0 - # TODO: allow these to be configured in yaml - self.high_threshold = 10 - self.med_threshold = 5 - self.low_threshold = 0 + if thresholds.get("file_access") is None: + self.low_file_access_threshold = 0 + self.med_file_access_threshold = 5 + self.high_file_access_threshold = 10 + else: + self._set_file_access_threshold( + thresholds=[ + thresholds.get("file_access")["low"], + thresholds.get("file_access")["medium"], + thresholds.get("file_access")["high"], + ] + ) + + def _set_file_access_threshold(self, thresholds: List[int]): + """ + Method that validates and then sets the file access threshold. + + :param: thresholds: The file access threshold to validate and set. + """ + if self._validate_thresholds( + thresholds=[ + thresholds[0], + thresholds[1], + thresholds[2], + ], + threshold_identifier="file_access", + ): + self.low_file_access_threshold = thresholds[0] + self.med_file_access_threshold = thresholds[1] + self.high_file_access_threshold = thresholds[2] def _categorise_num_access(self, num_access: int) -> int: """ @@ -60,11 +92,11 @@ class FileObservation(AbstractObservation, discriminator="file"): :param num_access: Number of file accesses. :return: Bin number corresponding to the number of accesses. """ - if num_access > self.high_threshold: + if num_access > self.high_file_access_threshold: return 3 - elif num_access > self.med_threshold: + elif num_access > self.med_file_access_threshold: return 2 - elif num_access > self.low_threshold: + elif num_access > self.low_file_access_threshold: return 1 return 0 @@ -122,6 +154,7 @@ class FileObservation(AbstractObservation, discriminator="file"): where=parent_where + ["files", config.file_name], include_num_access=config.include_num_access, file_system_requires_scan=config.file_system_requires_scan, + thresholds=config.thresholds, ) @@ -149,6 +182,7 @@ class FolderObservation(AbstractObservation, discriminator="folder"): num_files: int, include_num_access: bool, file_system_requires_scan: bool, + thresholds: Optional[Dict] = {}, ) -> None: """ Initialise a folder observation instance. @@ -177,6 +211,7 @@ class FolderObservation(AbstractObservation, discriminator="folder"): where=None, include_num_access=include_num_access, file_system_requires_scan=self.file_system_requires_scan, + thresholds=thresholds, ) ) while len(self.files) > num_files: @@ -253,6 +288,7 @@ class FolderObservation(AbstractObservation, discriminator="folder"): for file_config in config.files: file_config.include_num_access = config.include_num_access file_config.file_system_requires_scan = config.file_system_requires_scan + file_config.thresholds = config.thresholds files = [FileObservation.from_config(config=f, parent_where=where) for f in config.files] return cls( @@ -261,4 +297,5 @@ class FolderObservation(AbstractObservation, discriminator="folder"): num_files=config.num_files, include_num_access=config.include_num_access, file_system_requires_scan=config.file_system_requires_scan, + thresholds=config.thresholds, ) diff --git a/src/primaite/game/agent/observations/host_observations.py b/src/primaite/game/agent/observations/host_observations.py index 17bcb983..9b979063 100644 --- a/src/primaite/game/agent/observations/host_observations.py +++ b/src/primaite/game/agent/observations/host_observations.py @@ -54,7 +54,15 @@ class HostObservation(AbstractObservation, discriminator="host"): """ If True, files and folders must be scanned to update the health state. If False, true state is always shown. """ - include_users: Optional[bool] = None + services_requires_scan: Optional[bool] = None + """ + If True, services must be scanned to update the health state. If False, true state is always shown. + """ + applications_requires_scan: Optional[bool] = None + """ + If True, applications must be scanned to update the health state. If False, true state is always shown. + """ + include_users: Optional[bool] = True """If True, report user session information.""" def __init__( @@ -73,6 +81,8 @@ class HostObservation(AbstractObservation, discriminator="host"): monitored_traffic: Optional[Dict], include_num_access: bool, file_system_requires_scan: bool, + services_requires_scan: bool, + applications_requires_scan: bool, include_users: bool, ) -> None: """ @@ -108,6 +118,12 @@ class HostObservation(AbstractObservation, discriminator="host"): :param file_system_requires_scan: If True, the files and folders must be scanned to update the health state. If False, the true state is always shown. :type file_system_requires_scan: bool + :param services_requires_scan: If True, services must be scanned to update the health state. + If False, the true state is always shown. + :type services_requires_scan: bool + :param applications_requires_scan: If True, applications must be scanned to update the health state. + If False, the true state is always shown. + :type applications_requires_scan: bool :param include_users: If True, report user session information. :type include_users: bool """ @@ -121,7 +137,7 @@ class HostObservation(AbstractObservation, discriminator="host"): # Ensure lists have lengths equal to specified counts by truncating or padding self.services: List[ServiceObservation] = services while len(self.services) < num_services: - self.services.append(ServiceObservation(where=None)) + self.services.append(ServiceObservation(where=None, services_requires_scan=services_requires_scan)) while len(self.services) > num_services: truncated_service = self.services.pop() msg = f"Too many services in Node observation space for node. Truncating service {truncated_service.where}" @@ -129,7 +145,9 @@ class HostObservation(AbstractObservation, discriminator="host"): self.applications: List[ApplicationObservation] = applications while len(self.applications) < num_applications: - self.applications.append(ApplicationObservation(where=None)) + self.applications.append( + ApplicationObservation(where=None, applications_requires_scan=applications_requires_scan) + ) while len(self.applications) > num_applications: truncated_application = self.applications.pop() msg = f"Too many applications in Node observation space for node. Truncating {truncated_application.where}" @@ -153,7 +171,13 @@ class HostObservation(AbstractObservation, discriminator="host"): self.nics: List[NICObservation] = network_interfaces while len(self.nics) < num_nics: - self.nics.append(NICObservation(where=None, include_nmne=include_nmne, monitored_traffic=monitored_traffic)) + self.nics.append( + NICObservation( + where=None, + include_nmne=include_nmne, + monitored_traffic=monitored_traffic, + ) + ) while len(self.nics) > num_nics: truncated_nic = self.nics.pop() msg = f"Too many network_interfaces in Node observation space for node. Truncating {truncated_nic.where}" @@ -269,8 +293,15 @@ class HostObservation(AbstractObservation, discriminator="host"): folder_config.include_num_access = config.include_num_access folder_config.num_files = config.num_files folder_config.file_system_requires_scan = config.file_system_requires_scan + folder_config.thresholds = config.thresholds for nic_config in config.network_interfaces: nic_config.include_nmne = config.include_nmne + nic_config.thresholds = config.thresholds + for service_config in config.services: + service_config.services_requires_scan = config.services_requires_scan + for application_config in config.applications: + application_config.applications_requires_scan = config.applications_requires_scan + application_config.thresholds = config.thresholds services = [ServiceObservation.from_config(config=c, parent_where=where) for c in config.services] applications = [ApplicationObservation.from_config(config=c, parent_where=where) for c in config.applications] @@ -281,7 +312,10 @@ class HostObservation(AbstractObservation, discriminator="host"): count = 1 while len(nics) < config.num_nics: nic_config = NICObservation.ConfigSchema( - nic_num=count, include_nmne=config.include_nmne, monitored_traffic=config.monitored_traffic + nic_num=count, + include_nmne=config.include_nmne, + monitored_traffic=config.monitored_traffic, + thresholds=config.thresholds, ) nics.append(NICObservation.from_config(config=nic_config, parent_where=where)) count += 1 @@ -301,5 +335,7 @@ class HostObservation(AbstractObservation, discriminator="host"): monitored_traffic=config.monitored_traffic, include_num_access=config.include_num_access, file_system_requires_scan=config.file_system_requires_scan, + services_requires_scan=config.services_requires_scan, + applications_requires_scan=config.applications_requires_scan, include_users=config.include_users, ) diff --git a/src/primaite/game/agent/observations/nic_observations.py b/src/primaite/game/agent/observations/nic_observations.py index 1aa6470d..8faeb906 100644 --- a/src/primaite/game/agent/observations/nic_observations.py +++ b/src/primaite/game/agent/observations/nic_observations.py @@ -1,13 +1,14 @@ # © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK from __future__ import annotations -from typing import Dict, List, Optional +from typing import ClassVar, Dict, List, Optional from gymnasium import spaces from gymnasium.core import ObsType from primaite.game.agent.observations.observations import AbstractObservation, WhereType from primaite.game.agent.utils import access_from_nested_dict, NOT_PRESENT_IN_STATE +from primaite.simulator.network.nmne import NMNEConfig from primaite.utils.validation.ip_protocol import IPProtocol from primaite.utils.validation.port import Port @@ -15,6 +16,9 @@ from primaite.utils.validation.port import Port class NICObservation(AbstractObservation, discriminator="network-interface"): """Status information about a network interface within the simulation environment.""" + capture_nmne: ClassVar[bool] = NMNEConfig().capture_nmne + "A Boolean specifying whether malicious network events should be captured." + class ConfigSchema(AbstractObservation.ConfigSchema): """Configuration schema for NICObservation.""" @@ -25,7 +29,13 @@ class NICObservation(AbstractObservation, discriminator="network-interface"): monitored_traffic: Optional[Dict[IPProtocol, List[Port]]] = None """A dict containing which traffic types are to be included in the observation.""" - def __init__(self, where: WhereType, include_nmne: bool, monitored_traffic: Optional[Dict] = None) -> None: + def __init__( + self, + where: WhereType, + include_nmne: bool, + monitored_traffic: Optional[Dict] = None, + thresholds: Dict = {}, + ) -> None: """ Initialise a network interface observation instance. @@ -45,10 +55,18 @@ class NICObservation(AbstractObservation, discriminator="network-interface"): self.nmne_inbound_last_step: int = 0 self.nmne_outbound_last_step: int = 0 - # TODO: allow these to be configured in yaml - self.high_nmne_threshold = 10 - self.med_nmne_threshold = 5 - self.low_nmne_threshold = 0 + if thresholds.get("nmne") is None: + self.low_nmne_threshold = 0 + self.med_nmne_threshold = 5 + self.high_nmne_threshold = 10 + else: + self._set_nmne_threshold( + thresholds=[ + thresholds.get("nmne")["low"], + thresholds.get("nmne")["medium"], + thresholds.get("nmne")["high"], + ] + ) self.monitored_traffic = monitored_traffic if self.monitored_traffic: @@ -105,6 +123,20 @@ class NICObservation(AbstractObservation, discriminator="network-interface"): bandwidth_utilisation = traffic_value / nic_max_bandwidth return int(bandwidth_utilisation * 9) + 1 + def _set_nmne_threshold(self, thresholds: List[int]): + """ + Method that validates and then sets the NMNE threshold. + + :param: thresholds: The NMNE threshold to validate and set. + """ + if self._validate_thresholds( + thresholds=thresholds, + threshold_identifier="nmne", + ): + self.low_nmne_threshold = thresholds[0] + self.med_nmne_threshold = thresholds[1] + self.high_nmne_threshold = thresholds[2] + def observe(self, state: Dict) -> ObsType: """ Generate observation based on the current state of the simulation. @@ -116,7 +148,7 @@ class NICObservation(AbstractObservation, discriminator="network-interface"): """ nic_state = access_from_nested_dict(state, self.where) - if nic_state is NOT_PRESENT_IN_STATE: + if nic_state is NOT_PRESENT_IN_STATE or self.where is None: return self.default_observation obs = {"nic_status": 1 if nic_state["enabled"] else 2} @@ -164,7 +196,7 @@ class NICObservation(AbstractObservation, discriminator="network-interface"): for port in self.monitored_traffic[protocol]: obs["TRAFFIC"][protocol][port] = {"inbound": 0, "outbound": 0} - if self.include_nmne: + if self.capture_nmne and self.include_nmne: obs.update({"NMNE": {}}) direction_dict = nic_state["nmne"].get("direction", {}) inbound_keywords = direction_dict.get("inbound", {}).get("keywords", {}) @@ -224,6 +256,7 @@ class NICObservation(AbstractObservation, discriminator="network-interface"): where=parent_where + ["NICs", config.nic_num], include_nmne=config.include_nmne, monitored_traffic=config.monitored_traffic, + thresholds=config.thresholds, ) diff --git a/src/primaite/game/agent/observations/node_observations.py b/src/primaite/game/agent/observations/node_observations.py index 3a3283a2..260fac68 100644 --- a/src/primaite/game/agent/observations/node_observations.py +++ b/src/primaite/game/agent/observations/node_observations.py @@ -48,7 +48,13 @@ class NodesObservation(AbstractObservation, discriminator="nodes"): include_num_access: Optional[bool] = None """Flag to include the number of accesses.""" file_system_requires_scan: bool = True - """If True, the folder must be scanned to update the health state. Tf False, the true state is always shown.""" + """If True, the folder must be scanned to update the health state. If False, the true state is always shown.""" + services_requires_scan: bool = True + """If True, the services must be scanned to update the health state. + If False, the true state is always shown.""" + applications_requires_scan: bool = True + """If True, the applications must be scanned to update the health state. + If False, the true state is always shown.""" include_users: Optional[bool] = True """If True, report user session information.""" num_ports: Optional[int] = None @@ -196,8 +202,14 @@ class NodesObservation(AbstractObservation, discriminator="nodes"): host_config.include_num_access = config.include_num_access if host_config.file_system_requires_scan is None: host_config.file_system_requires_scan = config.file_system_requires_scan + if host_config.services_requires_scan is None: + host_config.services_requires_scan = config.services_requires_scan + if host_config.applications_requires_scan is None: + host_config.applications_requires_scan = config.applications_requires_scan if host_config.include_users is None: host_config.include_users = config.include_users + if not host_config.thresholds: + host_config.thresholds = config.thresholds for router_config in config.routers: if router_config.num_ports is None: @@ -214,6 +226,8 @@ class NodesObservation(AbstractObservation, discriminator="nodes"): router_config.num_rules = config.num_rules if router_config.include_users is None: router_config.include_users = config.include_users + if not router_config.thresholds: + router_config.thresholds = config.thresholds for firewall_config in config.firewalls: if firewall_config.ip_list is None: @@ -228,6 +242,8 @@ class NodesObservation(AbstractObservation, discriminator="nodes"): firewall_config.num_rules = config.num_rules if firewall_config.include_users is None: firewall_config.include_users = config.include_users + if not firewall_config.thresholds: + firewall_config.thresholds = config.thresholds hosts = [HostObservation.from_config(config=c, parent_where=where) for c in config.hosts] routers = [RouterObservation.from_config(config=c, parent_where=where) for c in config.routers] diff --git a/src/primaite/game/agent/observations/observation_manager.py b/src/primaite/game/agent/observations/observation_manager.py index 032435b8..e8cb18aa 100644 --- a/src/primaite/game/agent/observations/observation_manager.py +++ b/src/primaite/game/agent/observations/observation_manager.py @@ -114,7 +114,9 @@ class NestedObservation(AbstractObservation, discriminator="custom"): instances = dict() for component in config.components: obs_class = AbstractObservation._registry[component.type] - obs_instance = obs_class.from_config(config=obs_class.ConfigSchema(**component.options)) + obs_instance = obs_class.from_config( + config=obs_class.ConfigSchema(**component.options, thresholds=config.thresholds) + ) instances[component.label] = obs_instance return cls(components=instances) @@ -242,8 +244,5 @@ class ObservationManager(BaseModel): """ if config is None: return cls(NullObservation()) - obs_type = config["type"] - obs_class = AbstractObservation._registry[obs_type] - observation = obs_class.from_config(config=obs_class.ConfigSchema(**config["options"])) - obs_manager = cls(observation) + obs_manager = cls(config=config) return obs_manager diff --git a/src/primaite/game/agent/observations/observations.py b/src/primaite/game/agent/observations/observations.py index da81d2ad..8558b75c 100644 --- a/src/primaite/game/agent/observations/observations.py +++ b/src/primaite/game/agent/observations/observations.py @@ -1,7 +1,7 @@ # © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK """Manages the observation space for the agent.""" from abc import ABC, abstractmethod -from typing import Any, Dict, Iterable, Optional, Type, Union +from typing import Any, Dict, Iterable, List, Optional, Type, Union from gymnasium import spaces from gymnasium.core import ObsType @@ -19,6 +19,9 @@ class AbstractObservation(ABC): class ConfigSchema(ABC, BaseModel): """Config schema for observations.""" + thresholds: Optional[Dict] = {} + """A dict containing the observation thresholds.""" + model_config = ConfigDict(extra="forbid") _registry: Dict[str, Type["AbstractObservation"]] = {} @@ -69,3 +72,34 @@ class AbstractObservation(ABC): def from_config(cls, config: ConfigSchema, parent_where: WhereType = []) -> "AbstractObservation": """Create this observation space component form a serialised format.""" return cls() + + def _validate_thresholds(self, thresholds: List[int] = None, threshold_identifier: Optional[str] = "") -> bool: + """ + Method that checks if the thresholds are non overlapping and in the correct (ascending) order. + + Pass in the thresholds from low to high e.g. + thresholds=[low_threshold, med_threshold, ..._threshold, high_threshold] + + Throws an error if the threshold is not valid + + :param: thresholds: List of thresholds in ascending order. + :type: List[int] + :param: threshold_identifier: The name of the threshold option. + :type: Optional[str] + + :returns: bool + """ + if thresholds is None or len(thresholds) < 2: + raise Exception(f"{threshold_identifier} thresholds are invalid {thresholds}") + for idx in range(1, len(thresholds)): + if not isinstance(thresholds[idx], int): + raise Exception(f"{threshold_identifier} threshold ({thresholds[idx]}) is not a valid int.") + if not isinstance(thresholds[idx - 1], int): + raise Exception(f"{threshold_identifier} threshold ({thresholds[idx]}) is not a valid int.") + + if thresholds[idx] <= thresholds[idx - 1]: + raise Exception( + f"{threshold_identifier} threshold ({thresholds[idx - 1]}) " + f"is greater than or equal to ({thresholds[idx]}.)" + ) + return True diff --git a/src/primaite/game/agent/observations/software_observation.py b/src/primaite/game/agent/observations/software_observation.py index 07ec1abf..dac6b362 100644 --- a/src/primaite/game/agent/observations/software_observation.py +++ b/src/primaite/game/agent/observations/software_observation.py @@ -1,7 +1,7 @@ # © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK from __future__ import annotations -from typing import Dict +from typing import Dict, List, Optional from gymnasium import spaces from gymnasium.core import ObsType @@ -19,7 +19,10 @@ class ServiceObservation(AbstractObservation, discriminator="service"): service_name: str """Name of the service, used for querying simulation state dictionary""" - def __init__(self, where: WhereType) -> None: + services_requires_scan: Optional[bool] = None + """If True, services must be scanned to update the health state. If False, true state is always shown.""" + + def __init__(self, where: WhereType, services_requires_scan: bool) -> None: """ Initialise a service observation instance. @@ -28,6 +31,7 @@ class ServiceObservation(AbstractObservation, discriminator="service"): :type where: WhereType """ self.where = where + self.services_requires_scan = services_requires_scan self.default_observation = {"operating_status": 0, "health_status": 0} def observe(self, state: Dict) -> ObsType: @@ -44,7 +48,9 @@ class ServiceObservation(AbstractObservation, discriminator="service"): return self.default_observation return { "operating_status": service_state["operating_state"], - "health_status": service_state["health_state_visible"], + "health_status": service_state["health_state_visible"] + if self.services_requires_scan + else service_state["health_state_actual"], } @property @@ -70,7 +76,9 @@ class ServiceObservation(AbstractObservation, discriminator="service"): :return: Constructed service observation instance. :rtype: ServiceObservation """ - return cls(where=parent_where + ["services", config.service_name]) + return cls( + where=parent_where + ["services", config.service_name], services_requires_scan=config.services_requires_scan + ) class ApplicationObservation(AbstractObservation, discriminator="application"): @@ -82,7 +90,12 @@ class ApplicationObservation(AbstractObservation, discriminator="application"): application_name: str """Name of the application, used for querying simulation state dictionary""" - def __init__(self, where: WhereType) -> None: + applications_requires_scan: Optional[bool] = None + """ + If True, applications must be scanned to update the health state. If False, true state is always shown. + """ + + def __init__(self, where: WhereType, applications_requires_scan: bool, thresholds: Optional[Dict] = {}) -> None: """ Initialise an application observation instance. @@ -92,25 +105,52 @@ class ApplicationObservation(AbstractObservation, discriminator="application"): :type where: WhereType """ self.where = where + self.applications_requires_scan = applications_requires_scan self.default_observation = {"operating_status": 0, "health_status": 0, "num_executions": 0} - # TODO: allow these to be configured in yaml - self.high_threshold = 10 - self.med_threshold = 5 - self.low_threshold = 0 + if thresholds.get("app_executions") is None: + self.low_app_execution_threshold = 0 + self.med_app_execution_threshold = 5 + self.high_app_execution_threshold = 10 + else: + self._set_application_execution_thresholds( + thresholds=[ + thresholds.get("app_executions")["low"], + thresholds.get("app_executions")["medium"], + thresholds.get("app_executions")["high"], + ] + ) + + def _set_application_execution_thresholds(self, thresholds: List[int]): + """ + Method that validates and then sets the application execution threshold. + + :param: thresholds: The application execution threshold to validate and set. + """ + if self._validate_thresholds( + thresholds=[ + thresholds[0], + thresholds[1], + thresholds[2], + ], + threshold_identifier="app_executions", + ): + self.low_app_execution_threshold = thresholds[0] + self.med_app_execution_threshold = thresholds[1] + self.high_app_execution_threshold = thresholds[2] def _categorise_num_executions(self, num_executions: int) -> int: """ - Represent number of file accesses as a categorical variable. + Represent number of application executions as a categorical variable. - :param num_access: Number of file accesses. + :param num_access: Number of application executions. :return: Bin number corresponding to the number of accesses. """ - if num_executions > self.high_threshold: + if num_executions > self.high_app_execution_threshold: return 3 - elif num_executions > self.med_threshold: + elif num_executions > self.med_app_execution_threshold: return 2 - elif num_executions > self.low_threshold: + elif num_executions > self.low_app_execution_threshold: return 1 return 0 @@ -128,7 +168,9 @@ class ApplicationObservation(AbstractObservation, discriminator="application"): return self.default_observation return { "operating_status": application_state["operating_state"], - "health_status": application_state["health_state_visible"], + "health_status": application_state["health_state_visible"] + if self.applications_requires_scan + else application_state["health_state_actual"], "num_executions": self._categorise_num_executions(application_state["num_executions"]), } @@ -161,4 +203,8 @@ class ApplicationObservation(AbstractObservation, discriminator="application"): :return: Constructed application observation instance. :rtype: ApplicationObservation """ - return cls(where=parent_where + ["applications", config.application_name]) + return cls( + where=parent_where + ["applications", config.application_name], + applications_requires_scan=config.applications_requires_scan, + thresholds=config.thresholds, + ) diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index 1427776e..a3a790cc 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -7,12 +7,14 @@ from pydantic import BaseModel, ConfigDict from primaite import DEFAULT_BANDWIDTH, getLogger from primaite.game.agent.interface import AbstractAgent, ProxyAgent +from primaite.game.agent.observations import NICObservation from primaite.game.agent.rewards import SharedReward from primaite.game.science import graph_has_cycle, topological_sort from primaite.simulator import SIM_OUTPUT from primaite.simulator.network.creation import NetworkNodeAdder from primaite.simulator.network.hardware.base import NetworkInterface, Node, NodeOperatingState, UserManager from primaite.simulator.network.hardware.nodes.host.host_node import NIC +from primaite.simulator.network.hardware.nodes.network.firewall import Firewall # noqa: F401 from primaite.simulator.network.hardware.nodes.network.switch import Switch from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter from primaite.simulator.network.nmne import NMNEConfig @@ -44,15 +46,15 @@ from primaite.utils.validation.port import Port, PORT_LOOKUP _LOGGER = getLogger(__name__) SERVICE_TYPES_MAPPING = { - "DNSClient": DNSClient, - "DNSServer": DNSServer, - "DatabaseService": DatabaseService, - "WebServer": WebServer, - "FTPClient": FTPClient, - "FTPServer": FTPServer, - "NTPClient": NTPClient, - "NTPServer": NTPServer, - "Terminal": Terminal, + "dns-client": DNSClient, + "dns-server": DNSServer, + "database-service": DatabaseService, + "web-server": WebServer, + "ftp-client": FTPClient, + "ftp-server": FTPServer, + "ntp-client": NTPClient, + "ntp-server": NTPServer, + "terminal": Terminal, } """List of available services that can be installed on nodes in the PrimAITE Simulation.""" @@ -68,6 +70,8 @@ class PrimaiteGameOptions(BaseModel): seed: int = None """Random number seed for RNGs.""" + generate_seed_value: bool = False + """Internally generated seed value.""" max_episode_length: int = 256 """Maximum number of episodes for the PrimAITE game.""" ports: List[Port] @@ -175,6 +179,7 @@ class PrimaiteGame: parameters=parameters, request=request, response=response, + observation=obs, ) def pre_timestep(self) -> None: @@ -263,6 +268,7 @@ class PrimaiteGame: node_sets_cfg = network_config.get("node_sets", []) # Set the NMNE capture config NetworkInterface.nmne_config = NMNEConfig(**network_config.get("nmne_config", {})) + NICObservation.capture_nmne = NMNEConfig(**network_config.get("nmne_config", {})).capture_nmne for node_cfg in nodes_cfg: n_type = node_cfg["type"] @@ -293,6 +299,7 @@ class PrimaiteGame: if "users" in node_cfg and new_node.software_manager.software.get("user-manager"): user_manager: UserManager = new_node.software_manager.software["user-manager"] # noqa + for user_cfg in node_cfg["users"]: user_manager.add_user(**user_cfg, bypass_can_perform_action=True) @@ -407,6 +414,7 @@ class PrimaiteGame: agents_cfg = cfg.get("agents", []) for agent_cfg in agents_cfg: + agent_cfg = {**agent_cfg, "thresholds": game.options.thresholds} new_agent = AbstractAgent.from_config(agent_cfg) game.agents[agent_cfg["ref"]] = new_agent if isinstance(new_agent, ProxyAgent): diff --git a/src/primaite/notebooks/Command-and-Control-E2E-Demonstration.ipynb b/src/primaite/notebooks/Command-and-Control-E2E-Demonstration.ipynb index ef4e75dd..52499ea6 100644 --- a/src/primaite/notebooks/Command-and-Control-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Command-and-Control-E2E-Demonstration.ipynb @@ -50,40 +50,22 @@ "custom_c2_agent = \"\"\"\n", " - ref: CustomC2Agent\n", " team: RED\n", - " type: ProxyAgent\n", + " type: proxy-a.gent\n", "\n", " action_space:\n", - " options:\n", - " nodes:\n", - " - node_name: web_server\n", - " applications:\n", - " - application_name: C2Beacon\n", - " - node_name: client_1\n", - " applications:\n", - " - application_name: C2Server\n", - " max_folders_per_node: 1\n", - " max_files_per_folder: 1\n", - " max_services_per_node: 2\n", - " max_nics_per_node: 8\n", - " max_acl_rules: 10\n", - " ip_list:\n", - " - 192.168.1.21\n", - " - 192.168.1.14\n", - " wildcard_list:\n", - " - 0.0.0.1\n", " action_map:\n", " 0:\n", " action: do_nothing\n", " options: {}\n", " 1:\n", - " action: node_application_install\n", + " action: node-application-install\n", " options:\n", - " node_id: 0\n", - " application_name: C2Beacon\n", + " node_name: web_server\n", + " application_name: c2-beacon\n", " 2:\n", - " action: configure_c2_beacon\n", + " action: configure-c2-beacon\n", " options:\n", - " node_id: 0\n", + " node_name: web_server\n", " config:\n", " c2_server_ip_address: 192.168.10.21\n", " keep_alive_frequency:\n", @@ -92,10 +74,10 @@ " 3:\n", " action: node_application_execute\n", " options:\n", - " node_id: 0\n", - " application_id: 0\n", + " node_name: web_server\n", + " application_name: c2-beacon\n", " 4:\n", - " action: c2_server_terminal_command\n", + " action: c2-server-terminal-command\n", " options:\n", " node_id: 1\n", " ip_address:\n", @@ -111,14 +93,14 @@ " 5:\n", " action: c2-server-ransomware-configure\n", " options:\n", - " node_id: 1\n", + " node_name: client_1\n", " config:\n", " server_ip_address: 192.168.1.14\n", " payload: ENCRYPT\n", " 6:\n", - " action: c2_server_data_exfiltrate\n", + " action: c2-server-data-exfiltrate\n", " options:\n", - " node_id: 1\n", + " node_name: client_1\n", " target_file_name: \"database.db\"\n", " target_folder_name: \"database\"\n", " exfiltration_folder_name: \"spoils\"\n", @@ -128,31 +110,27 @@ " password: admin\n", "\n", " 7:\n", - " action: c2_server_ransomware_launch\n", + " action: c2-server-ransomware-launch\n", " options:\n", - " node_id: 1\n", + " node_name: client_1\n", " 8:\n", - " action: configure_c2_beacon\n", + " action: configure-c2-beacon\n", " options:\n", - " node_id: 0\n", + " node_name: web_server\n", " config:\n", " c2_server_ip_address: 192.168.10.21\n", " keep_alive_frequency: 10\n", " masquerade_protocol: TCP\n", " masquerade_port: DNS\n", " 9:\n", - " action: configure_c2_beacon\n", + " action: configure-c2-beacon\n", " options:\n", - " node_id: 0\n", + " node_name: web_server\n", " config:\n", " c2_server_ip_address: 192.168.10.22\n", " keep_alive_frequency:\n", " masquerade_protocol:\n", " masquerade_port:\n", - "\n", - " reward_function:\n", - " reward_components:\n", - " - type: DUMMY\n", "\"\"\"\n", "c2_agent_yaml = yaml.safe_load(custom_c2_agent)" ] @@ -225,7 +203,7 @@ " nodes: # Node List\n", " - node_name: web_server\n", " applications: \n", - " - application_name: C2Beacon\n", + " - application_name: c2-beacon\n", " ...\n", " ...\n", " action_map:\n", @@ -233,7 +211,7 @@ " action: node_application_install \n", " options:\n", " node_id: 0 # Index 0 at the node list.\n", - " application_name: C2Beacon\n", + " application_name: c2-beacon\n", "```" ] }, @@ -268,7 +246,7 @@ " action_map:\n", " ...\n", " 2:\n", - " action: configure_c2_beacon\n", + " action: configure-c2-beacon\n", " options:\n", " node_id: 0 # Node Index\n", " config: # Further information about these config options can be found at the bottom of this notebook.\n", @@ -286,7 +264,7 @@ "outputs": [], "source": [ "env.step(2)\n", - "c2_beacon: C2Beacon = web_server.software_manager.software[\"C2Beacon\"]\n", + "c2_beacon: C2Beacon = web_server.software_manager.software[\"c2-beacon\"]\n", "web_server.software_manager.show()\n", "c2_beacon.show()" ] @@ -307,13 +285,13 @@ " nodes: # Node List\n", " - node_name: web_server\n", " applications: \n", - " - application_name: C2Beacon\n", + " - application_name: c2-beacon\n", " ...\n", " ...\n", " action_map:\n", " ...\n", " 3:\n", - " action: node_application_execute\n", + " action: node-application-execute\n", " options:\n", " node_id: 0\n", " application_id: 0\n", @@ -374,11 +352,11 @@ " ...\n", " - node_name: client_1\n", " applications: \n", - " - application_name: C2Server\n", + " - application_name: c2-server\n", " ...\n", " action_map:\n", " 4:\n", - " action: C2_SERVER_TERMINAL_COMMAND\n", + " action: c2-server-terminal-command\n", " options:\n", " node_id: 1\n", " ip_address:\n", @@ -431,7 +409,7 @@ " ...\n", " - node_name: client_1\n", " applications: \n", - " - application_name: C2Server\n", + " - application_name: c2-server\n", " ...\n", " action_map:\n", " 5:\n", @@ -459,7 +437,7 @@ "metadata": {}, "outputs": [], "source": [ - "ransomware_script: RansomwareScript = web_server.software_manager.software[\"RansomwareScript\"]\n", + "ransomware_script: RansomwareScript = web_server.software_manager.software[\"ransomware-script\"]\n", "web_server.software_manager.show()\n", "ransomware_script.show()" ] @@ -483,11 +461,11 @@ " ...\n", " - node_name: client_1\n", " applications: \n", - " - application_name: C2Server\n", + " - application_name: c2-server\n", " ...\n", " action_map:\n", " 6:\n", - " action: c2_server_data_exfiltrate\n", + " action: c2-server-data-exfiltrate\n", " options:\n", " node_id: 1\n", " target_file_name: \"database.db\"\n", @@ -549,11 +527,11 @@ " ...\n", " - node_name: client_1\n", " applications: \n", - " - application_name: C2Server\n", + " - application_name: c2-server\n", " ...\n", " action_map:\n", " 7:\n", - " action: c2_server_ransomware_launch\n", + " action: c2-server-ransomware-launch\n", " options:\n", " node_id: 1\n", "```\n" @@ -598,20 +576,20 @@ "custom_blue_agent_yaml = \"\"\"\n", " - ref: defender\n", " team: BLUE\n", - " type: ProxyAgent\n", + " type: proxy-agent\n", "\n", " observation_space:\n", - " type: CUSTOM\n", + " type: custom\n", " options:\n", " components:\n", - " - type: NODES\n", + " - type: nodes\n", " label: NODES\n", " options:\n", " hosts:\n", " - hostname: web_server\n", " applications:\n", - " - application_name: C2Beacon\n", - " - application_name: RansomwareScript\n", + " - application_name: c2-beacon\n", + " - application_name: ransomware-script\n", " folders:\n", " - folder_name: exfiltration_folder\n", " files:\n", @@ -661,7 +639,7 @@ " - UDP\n", " num_rules: 10\n", "\n", - " - type: LINKS\n", + " - type: links\n", " label: LINKS\n", " options:\n", " link_references:\n", @@ -675,7 +653,7 @@ " - switch_2:eth-1<->client_1:eth-1\n", " - switch_2:eth-2<->client_2:eth-1\n", " - switch_2:eth-7<->security_suite:eth-2\n", - " - type: \"NONE\"\n", + " - type: \"none\"\n", " label: ICS\n", " options: {}\n", "\n", @@ -685,16 +663,16 @@ " action: do_nothing\n", " options: {}\n", " 1:\n", - " action: node_application_remove\n", + " action: node-application-remove\n", " options:\n", - " node_id: 0\n", + " node_name: web-server\n", " application_name: C2Beacon\n", " 2:\n", - " action: node_shutdown\n", + " action: node-shutdown\n", " options:\n", - " node_id: 0\n", + " node_name: web-server\n", " 3:\n", - " action: router_acl_add_rule\n", + " action: router-acl-add-rule\n", " options:\n", " target_router: router_1\n", " position: 1\n", @@ -707,36 +685,6 @@ " source_wildcard_id: 0\n", " dest_wildcard_id: 0\n", "\n", - "\n", - " options:\n", - " nodes:\n", - " - node_name: web_server\n", - " applications:\n", - " - application_name: C2Beacon\n", - "\n", - " - node_name: database_server\n", - " folders:\n", - " - folder_name: database\n", - " files:\n", - " - file_name: database.db\n", - " services:\n", - " - service_name: DatabaseService\n", - " - node_name: router_1\n", - "\n", - " max_folders_per_node: 2\n", - " max_files_per_folder: 2\n", - " max_services_per_node: 2\n", - " max_nics_per_node: 8\n", - " max_acl_rules: 10\n", - " ip_list:\n", - " - 192.168.10.21\n", - " - 192.168.1.12\n", - " wildcard_list:\n", - " - 0.0.0.1\n", - " reward_function:\n", - " reward_components:\n", - " - type: DUMMY\n", - "\n", " agent_settings:\n", " flatten_obs: False\n", "\"\"\"\n", @@ -875,7 +823,7 @@ "outputs": [], "source": [ "# Installing RansomwareScript via C2 Terminal Commands\n", - "ransomware_install_command = {\"commands\":[[\"software_manager\", \"application\", \"install\", \"RansomwareScript\"]],\n", + "ransomware_install_command = {\"commands\":[[\"software_manager\", \"application\", \"install\", \"ransomware-script\"]],\n", " \"username\": \"admin\",\n", " \"password\": \"admin\"}\n", "c2_server.send_command(C2Command.TERMINAL, command_options=ransomware_install_command)\n" @@ -1034,11 +982,11 @@ " web_server: Server = given_env.game.simulation.network.get_node_by_hostname(\"web_server\")\n", "\n", " client_1.software_manager.install(C2Server)\n", - " c2_server: C2Server = client_1.software_manager.software[\"C2Server\"]\n", + " c2_server: C2Server = client_1.software_manager.software[\"c2-server\"]\n", " c2_server.run()\n", "\n", " web_server.software_manager.install(C2Beacon)\n", - " c2_beacon: C2Beacon = web_server.software_manager.software[\"C2Beacon\"]\n", + " c2_beacon: C2Beacon = web_server.software_manager.software[\"c2-beacon\"]\n", " c2_beacon.configure(c2_server_ip_address=\"192.168.10.21\")\n", " c2_beacon.establish()\n", "\n", @@ -1132,11 +1080,11 @@ "outputs": [], "source": [ "# Attempting to install the C2 RansomwareScript\n", - "ransomware_install_command = {\"commands\":[[\"software_manager\", \"application\", \"install\", \"RansomwareScript\"]],\n", + "ransomware_install_command = {\"commands\":[[\"software_manager\", \"application\", \"install\", \"ransomware-script\"]],\n", " \"username\": \"admin\",\n", " \"password\": \"admin\"}\n", "\n", - "c2_server: C2Server = client_1.software_manager.software[\"C2Server\"]\n", + "c2_server: C2Server = client_1.software_manager.software[\"c2-server\"]\n", "c2_server.send_command(C2Command.TERMINAL, command_options=ransomware_install_command)" ] }, @@ -1220,11 +1168,11 @@ "outputs": [], "source": [ "# Attempting to install the C2 RansomwareScript\n", - "ransomware_install_command = {\"commands\":[\"software_manager\", \"application\", \"install\", \"RansomwareScript\"],\n", + "ransomware_install_command = {\"commands\":[\"software_manager\", \"application\", \"install\", \"ransomware-script\"],\n", " \"username\": \"admin\",\n", " \"password\": \"admin\"}\n", "\n", - "c2_server: C2Server = client_1.software_manager.software[\"C2Server\"]\n", + "c2_server: C2Server = client_1.software_manager.software[\"c2-server\"]\n", "c2_server.send_command(C2Command.TERMINAL, command_options=ransomware_install_command)" ] }, @@ -1345,7 +1293,7 @@ "metadata": {}, "outputs": [], "source": [ - "database_server: Server = blue_env.game.simulation.network.get_node_by_hostname(\"database_server\")\n", + "database_server: Server = blue_env.game.simulation.network.get_node_by_hostname(\"database-server\")\n", "database_server.software_manager.file_system.show(full=True)" ] }, @@ -1391,7 +1339,7 @@ "\n", "``` YAML\n", "...\n", - " action: configure_c2_beacon\n", + " action: configure-c2-beacon\n", " options:\n", " node_id: 0\n", " config:\n", @@ -1446,16 +1394,16 @@ "source": [ "web_server: Server = c2_config_env.game.simulation.network.get_node_by_hostname(\"web_server\")\n", "web_server.software_manager.install(C2Beacon)\n", - "c2_beacon: C2Beacon = web_server.software_manager.software[\"C2Beacon\"]\n", + "c2_beacon: C2Beacon = web_server.software_manager.software[\"c2-beacon\"]\n", "\n", "client_1: Computer = c2_config_env.game.simulation.network.get_node_by_hostname(\"client_1\")\n", "client_1.software_manager.install(C2Server)\n", - "c2_server_1: C2Server = client_1.software_manager.software[\"C2Server\"]\n", + "c2_server_1: C2Server = client_1.software_manager.software[\"c2-server\"]\n", "c2_server_1.run()\n", "\n", "client_2: Computer = c2_config_env.game.simulation.network.get_node_by_hostname(\"client_2\")\n", "client_2.software_manager.install(C2Server)\n", - "c2_server_2: C2Server = client_2.software_manager.software[\"C2Server\"]\n", + "c2_server_2: C2Server = client_2.software_manager.software[\"c2-server\"]\n", "c2_server_2.run()" ] }, @@ -1759,6 +1707,16 @@ "\n", "display_obs_diffs(tcp_c2_obs, udp_c2_obs, blue_config_env.game.step_counter)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "env.game.agents[\"CustomC2Agent\"].show_history()" + ] } ], "metadata": { diff --git a/src/primaite/notebooks/Data-Manipulation-Customising-Red-Agent.ipynb b/src/primaite/notebooks/Data-Manipulation-Customising-Red-Agent.ipynb index 756fc44f..2dbf750e 100644 --- a/src/primaite/notebooks/Data-Manipulation-Customising-Red-Agent.ipynb +++ b/src/primaite/notebooks/Data-Manipulation-Customising-Red-Agent.ipynb @@ -47,7 +47,7 @@ "source": [ "def make_cfg_have_flat_obs(cfg):\n", " for agent in cfg['agents']:\n", - " if agent['type'] == \"ProxyAgent\":\n", + " if agent['type'] == \"proxy-agent\":\n", " agent['agent_settings']['flatten_obs'] = False" ] }, @@ -76,9 +76,9 @@ " # parse the info dict form step output and write out what the red agent is doing\n", " red_info : AgentHistoryItem = info['agent_actions']['data_manipulation_attacker']\n", " red_action = red_info.action\n", - " if red_action == 'do_nothing':\n", + " if red_action == 'do-nothing':\n", " red_str = 'DO NOTHING'\n", - " elif red_action == 'node_application_execute':\n", + " elif red_action == 'node-application-execute':\n", " client = \"client 1\" if red_info.parameters['node_id'] == 0 else \"client 2\"\n", " red_str = f\"ATTACK from {client}\"\n", " return red_str" @@ -147,36 +147,14 @@ "```yaml\n", " - ref: data_manipulation_attacker # name of agent\n", " team: RED # not used, just for human reference\n", - " type: RedDatabaseCorruptingAgent # type of agent - this lets primaite know which agent class to use\n", + " type: red-database-corrupting-agent # type of agent - this lets primaite know which agent class to use\n", "\n", " # Since the agent does not need to react to what is happening in the environment, the observation space is empty.\n", " observation_space:\n", - " type: UC2RedObservation\n", + " type: uc2-red-observation # TODO: what\n", " options:\n", " nodes: {}\n", "\n", - " action_space:\n", - " \n", - " # The agent has access to the DataManipulationBoth on clients 1 and 2.\n", - " options:\n", - " nodes:\n", - " - node_name: client_1 # The network should have a node called client_1\n", - " applications:\n", - " - application_name: DataManipulationBot # The node client_1 should have DataManipulationBot configured on it\n", - " - node_name: client_2 # The network should have a node called client_2\n", - " applications:\n", - " - application_name: DataManipulationBot # The node client_2 should have DataManipulationBot configured on it\n", - "\n", - " # not important\n", - " max_folders_per_node: 1\n", - " max_files_per_folder: 1\n", - " max_services_per_node: 1\n", - "\n", - " # red agent does not need a reward function\n", - " reward_function:\n", - " reward_components:\n", - " - type: DUMMY\n", - "\n", " # These actions are passed to the RedDatabaseCorruptingAgent init method, they dictate the schedule of attacks\n", " agent_settings:\n", " start_settings:\n", @@ -211,15 +189,13 @@ " \n", " # \n", " applications:\n", - " - ref: data_manipulation_bot\n", - " type: DataManipulationBot\n", + " - type: data-manipulation-bot\n", " options:\n", " port_scan_p_of_success: 0.8 # Probability that port scan is successful\n", " data_manipulation_p_of_success: 0.8 # Probability that SQL attack is successful\n", " payload: \"DELETE\" # The SQL query which causes the attack (this has to be DELETE)\n", " server_ip: 192.168.1.14 # IP address of server hosting the database\n", - " - ref: client_1_database_client\n", - " type: DatabaseClient # Database client must be installed in order for DataManipulationBot to function\n", + " - type: database-client # Database client must be installed in order for DataManipulationBot to function\n", " options:\n", " db_server_ip: 192.168.1.14 # IP address of server hosting the database\n", "```" @@ -354,19 +330,16 @@ "# Make attack always succeed.\n", "change = yaml.safe_load(\"\"\"\n", " applications:\n", - " - ref: data_manipulation_bot\n", - " type: DataManipulationBot\n", + " - type: data-manipulation-bot\n", " options:\n", " port_scan_p_of_success: 1.0\n", " data_manipulation_p_of_success: 1.0\n", " payload: \"DELETE\"\n", " server_ip: 192.168.1.14\n", - " - ref: client_1_web_browser\n", - " type: WebBrowser\n", + " - type: web-browser\n", " options:\n", " target_url: http://arcd.com/users/\n", - " - ref: client_1_database_client\n", - " type: DatabaseClient\n", + " - type: database-client\n", " options:\n", " db_server_ip: 192.168.1.14\n", "\"\"\")\n", @@ -399,19 +372,16 @@ "# Make attack always fail.\n", "change = yaml.safe_load(\"\"\"\n", " applications:\n", - " - ref: data_manipulation_bot\n", - " type: DataManipulationBot\n", + " - type: data-manipulation-bot\n", " options:\n", " port_scan_p_of_success: 0.0\n", " data_manipulation_p_of_success: 0.0\n", " payload: \"DELETE\"\n", " server_ip: 192.168.1.14\n", - " - ref: client_1_web_browser\n", - " type: WebBrowser\n", + " - type: web-browser\n", " options:\n", " target_url: http://arcd.com/users/\n", - " - ref: client_1_database_client\n", - " type: DatabaseClient\n", + " - type: database-client\n", " options:\n", " db_server_ip: 192.168.1.14\n", "\"\"\")\n", diff --git a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb index dbc6f0c1..2070e03c 100644 --- a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb @@ -684,6 +684,15 @@ " print(f\"step: {env.game.step_counter}, Red action: {info['agent_actions']['data_manipulation_attacker'].action}, Blue reward:{reward:.2f}\" )" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.agents[\"data_manipulation_attacker\"].show_history()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -717,7 +726,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.10.11" } }, "nbformat": 4, diff --git a/src/primaite/notebooks/Getting-Information-Out-Of-PrimAITE.ipynb b/src/primaite/notebooks/Getting-Information-Out-Of-PrimAITE.ipynb index f8691d7d..58573ac6 100644 --- a/src/primaite/notebooks/Getting-Information-Out-Of-PrimAITE.ipynb +++ b/src/primaite/notebooks/Getting-Information-Out-Of-PrimAITE.ipynb @@ -153,6 +153,49 @@ "PRIMAITE_CONFIG[\"developer_mode\"][\"enabled\"] = was_enabled\n", "PRIMAITE_CONFIG[\"developer_mode\"][\"output_sys_logs\"] = was_syslogs_enabled" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Viewing Agent history\n", + "\n", + "It's possible to view the actions carried out by an agent for a given training session using the `show_history()` method. By default, this will be all actions apart from DONOTHING actions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(data_manipulation_config_path(), 'r') as f:\n", + " cfg = yaml.safe_load(f)\n", + "\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "\n", + "# Run the training session to generate some resultant data.\n", + "for i in range(100):\n", + " env.step(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Calling `.show_history()` should show us when the Data Manipulation used the `NODE_APPLICATION_EXECUTE` action." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "attacker = env.game.agents[\"data_manipulation_attacker\"]\n", + "\n", + "attacker.show_history()" + ] } ], "metadata": { @@ -171,7 +214,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.8" + "version": "3.10.11" } }, "nbformat": 4, diff --git a/src/primaite/notebooks/How-To-Use-Primaite-Dev-Mode.ipynb b/src/primaite/notebooks/How-To-Use-Primaite-Dev-Mode.ipynb new file mode 100644 index 00000000..8f8ec24b --- /dev/null +++ b/src/primaite/notebooks/How-To-Use-Primaite-Dev-Mode.ipynb @@ -0,0 +1,479 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# PrimAITE Developer mode\n", + "\n", + "PrimAITE has built in developer tools.\n", + "\n", + "The dev-mode is designed to help make the development of PrimAITE easier.\n", + "\n", + "`NOTE: For the purposes of the notebook, the commands are preceeded by \"!\". When running the commands, run it without the \"!\".`\n", + "\n", + "To display the available dev-mode options, run the command below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode --help" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Save the current PRIMAITE_CONFIG to restore after the notebook runs\n", + "\n", + "from primaite import PRIMAITE_CONFIG\n", + "\n", + "temp_config = PRIMAITE_CONFIG.copy()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dev mode options" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### enable\n", + "\n", + "Enables the dev mode for PrimAITE.\n", + "\n", + "This will enable the developer mode for PrimAITE.\n", + "\n", + "By default, when developer mode is enabled, session logs will be generated in the PRIMAITE_ROOT/sessions folder unless configured to be generated in another location." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode enable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### disable\n", + "\n", + "Disables the dev mode for PrimAITE.\n", + "\n", + "This will disable the developer mode for PrimAITE." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode disable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### show\n", + "\n", + "Shows if PrimAITE is running in dev mode or production mode.\n", + "\n", + "The command will also show the developer mode configuration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode show" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### config\n", + "\n", + "Configure the PrimAITE developer mode" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --help" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### path\n", + "\n", + "Set the path where generated session files will be output.\n", + "\n", + "By default, this value will be in PRIMAITE_ROOT/sessions.\n", + "\n", + "To reset the path to default, run:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config path -root\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config path --default" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### --sys-log-level or -slevel\n", + "\n", + "Set the system log level.\n", + "\n", + "This will override the system log level in configurations and will make PrimAITE include the set log level and above.\n", + "\n", + "Available options are:\n", + "- `DEBUG`\n", + "- `INFO`\n", + "- `WARNING`\n", + "- `ERROR`\n", + "- `CRITICAL`\n", + "\n", + "Default value is `DEBUG`\n", + "\n", + "Example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --sys-log-level DEBUG\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -slevel DEBUG" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### --agent-log-level or -alevel\n", + "\n", + "Set the agent log level.\n", + "\n", + "This will override the agent log level in configurations and will make PrimAITE include the set log level and above.\n", + "\n", + "Available options are:\n", + "- `DEBUG`\n", + "- `INFO`\n", + "- `WARNING`\n", + "- `ERROR`\n", + "- `CRITICAL`\n", + "\n", + "Default value is `DEBUG`\n", + "\n", + "Example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --agent-log-level DEBUG\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -alevel DEBUG" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### --output-sys-logs or -sys\n", + "\n", + "If enabled, developer mode will output system logs.\n", + "\n", + "Example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --output-sys-logs\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -sys" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To disable outputting sys logs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --no-sys-logs\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -nsys" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### --output-agent-logs or -agent\n", + "\n", + "If enabled, developer mode will output agent action logs.\n", + "\n", + "Example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --output-agent-logs\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -agent" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To disable outputting agent action logs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --no-agent-logs\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -nagent" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### --output-pcap-logs or -pcap\n", + "\n", + "If enabled, developer mode will output PCAP logs.\n", + "\n", + "Example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --output-pcap-logs\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -pcap" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To disable outputting PCAP logs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --no-pcap-logs\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -npcap" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### --output-to-terminal or -t\n", + "\n", + "If enabled, developer mode will output logs to the terminal.\n", + "\n", + "Example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --output-to-terminal\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -t" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To disable terminal outputs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config --no-terminal\n", + "\n", + "# or\n", + "\n", + "!primaite dev-mode config -nt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Combining commands\n", + "\n", + "It is possible to combine commands to set the configuration.\n", + "\n", + "This saves having to enter multiple commands and allows for a much more efficient setting of PrimAITE developer mode configurations." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Example of setting system log level and enabling the system logging:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config -slevel WARNING -sys" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another example where the system log and agent action log levels are set and enabled and should be printed to terminal:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite dev-mode config -slevel ERROR -sys -alevel ERROR -agent -t" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Restore PRIMAITE_CONFIG\n", + "from primaite.utils.cli.primaite_config_utils import update_primaite_application_config\n", + "\n", + "\n", + "global PRIMAITE_CONFIG\n", + "PRIMAITE_CONFIG[\"developer_mode\"] = temp_config[\"developer_mode\"]\n", + "update_primaite_application_config(config=PRIMAITE_CONFIG)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/Requests-and-Responses.ipynb b/src/primaite/notebooks/Requests-and-Responses.ipynb index 83aed07c..9260b29d 100644 --- a/src/primaite/notebooks/Requests-and-Responses.ipynb +++ b/src/primaite/notebooks/Requests-and-Responses.ipynb @@ -114,7 +114,7 @@ "metadata": {}, "outputs": [], "source": [ - "print(f\"DNS Client state: {client.software_manager.software.get('DNSClient').operating_state.name}\")" + "print(f\"DNS Client state: {client.software_manager.software.get('dns-client').operating_state.name}\")" ] }, { diff --git a/src/primaite/notebooks/Terminal-Processing.ipynb b/src/primaite/notebooks/Terminal-Processing.ipynb index 9aa4e96a..7c94d432 100644 --- a/src/primaite/notebooks/Terminal-Processing.ipynb +++ b/src/primaite/notebooks/Terminal-Processing.ipynb @@ -9,6 +9,13 @@ "© Crown-owned copyright 2024, Defence Science and Technology Laboratory UK" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulation Layer Implementation." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -67,9 +74,9 @@ "source": [ "network: Network = basic_network()\n", "computer_a: Computer = network.get_node_by_hostname(\"node_a\")\n", - "terminal_a: Terminal = computer_a.software_manager.software.get(\"Terminal\")\n", + "terminal_a: Terminal = computer_a.software_manager.software.get(\"terminal\")\n", "computer_b: Computer = network.get_node_by_hostname(\"node_b\")\n", - "terminal_b: Terminal = computer_b.software_manager.software.get(\"Terminal\")" + "terminal_b: Terminal = computer_b.software_manager.software.get(\"terminal\")" ] }, { @@ -121,7 +128,7 @@ "metadata": {}, "outputs": [], "source": [ - "term_a_term_b_remote_connection.execute([\"software_manager\", \"application\", \"install\", \"RansomwareScript\"])" + "term_a_term_b_remote_connection.execute([\"software_manager\", \"application\", \"install\", \"ransomware-script\"])" ] }, { @@ -169,6 +176,22 @@ "computer_b.file_system.show()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Information about the latest response when executing a remote command can be seen by calling the `last_response` attribute within `Terminal`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(terminal_a.last_response)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -207,6 +230,263 @@ "source": [ "computer_b.user_session_manager.show(include_historic=True, include_session_id=True)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Game Layer Implementation\n", + "\n", + "This notebook section will detail the implementation of how the game layer utilises the terminal to support different agent actions.\n", + "\n", + "The ``Terminal`` is used in a variety of different ways in the game layer. Specifically, the terminal is leveraged to implement the following actions:\n", + "\n", + "\n", + "| Game Layer Action | Simulation Layer |\n", + "|-----------------------------------|--------------------------|\n", + "| ``node-send-local-command`` | Uses the given user credentials, creates a ``LocalTerminalSession`` and executes the given command and returns the ``RequestResponse``.\n", + "| ``node-session-remote-login`` | Uses the given user credentials and remote IP to create a ``RemoteTerminalSession``.\n", + "| ``node-send-remote-command`` | Uses the given remote IP to locate the correct ``RemoteTerminalSession``, executes the given command and returns the ``RequestsResponse``." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Game Layer Setup\n", + "\n", + "Similar to other notebooks, the next code cells create a custom proxy agent to demonstrate how these commands can be leveraged by agents in the ``UC2`` network environment.\n", + "\n", + "If you're unfamiliar with ``UC2`` then please refer to the [UC2-E2E-Demo notebook for further reference](./Data-Manipulation-E2E-Demonstration.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "from primaite.config.load import data_manipulation_config_path\n", + "from primaite.session.environment import PrimaiteGymEnv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "custom_terminal_agent = \"\"\"\n", + " - ref: CustomC2Agent\n", + " team: RED\n", + " type: proxy-agent\n", + " observation_space: null\n", + " action_space:\n", + " options:\n", + " nodes:\n", + " - node_name: client_1\n", + " max_folders_per_node: 1\n", + " max_files_per_folder: 1\n", + " max_services_per_node: 2\n", + " max_nics_per_node: 8\n", + " max_acl_rules: 10\n", + " ip_list:\n", + " - 192.168.1.21\n", + " - 192.168.1.14\n", + " wildcard_list:\n", + " - 0.0.0.1\n", + " action_map:\n", + " 0:\n", + " action: do-nothing\n", + " options: {}\n", + " 1:\n", + " action: node-send-local-command\n", + " options:\n", + " node_name: client_1\n", + " username: admin\n", + " password: admin\n", + " command:\n", + " - file_system\n", + " - create\n", + " - file\n", + " - downloads\n", + " - dog.png\n", + " - False\n", + " 2:\n", + " action: node-session-remote-login\n", + " options:\n", + " node_name: client_1\n", + " username: admin\n", + " password: admin\n", + " remote_ip: 192.168.10.22\n", + " 3:\n", + " action: node-send-remote-command\n", + " options:\n", + " node_name: client_1\n", + " remote_ip: 192.168.10.22\n", + " command:\n", + " - file_system\n", + " - create\n", + " - file\n", + " - downloads\n", + " - cat.png\n", + " - False\n", + "\"\"\"\n", + "custom_terminal_agent_yaml = yaml.safe_load(custom_terminal_agent)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(data_manipulation_config_path()) as f:\n", + " cfg = yaml.safe_load(f)\n", + " # removing all agents & adding the custom agent.\n", + " cfg['agents'] = {}\n", + " cfg['agents'] = custom_terminal_agent_yaml\n", + "\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "\n", + "client_1: Computer = env.game.simulation.network.get_node_by_hostname(\"client_1\")\n", + "client_2: Computer = env.game.simulation.network.get_node_by_hostname(\"client_2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Terminal Action | ``node-send-local-command`` \n", + "\n", + "The yaml snippet below shows all the relevant agent options for this action:\n", + "\n", + "```yaml\n", + "\n", + " action_space:\n", + " action_list:\n", + " ...\n", + " - type: node-send-local-command\n", + " ...\n", + " options:\n", + " nodes: # Node List\n", + " - node_name: client_1\n", + " ...\n", + " ...\n", + " action_map:\n", + " 1:\n", + " action: node-send-local-command\n", + " options:\n", + " node_id: 0 # Index 0 at the node list.\n", + " username: admin\n", + " password: admin\n", + " command:\n", + " - file_system\n", + " - create\n", + " - file\n", + " - downloads\n", + " - dog.png\n", + " - False\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(1)\n", + "client_1.file_system.show(full=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Terminal Action | ``node-session-remote-login`` \n", + "\n", + "The yaml snippet below shows all the relevant agent options for this action:\n", + "\n", + "```yaml\n", + "\n", + " action_space:\n", + " action_list:\n", + " ...\n", + " - type: node-session-remote-login\n", + " ...\n", + " options:\n", + " nodes: # Node List\n", + " - node_name: client_1\n", + " ...\n", + " ...\n", + " action_map:\n", + " 2:\n", + " action: node-session-remote-login\n", + " options:\n", + " node_id: 0 # Index 0 at the node list.\n", + " username: admin\n", + " password: admin\n", + " remote_ip: 192.168.10.22 # client_2's ip address.\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(2)\n", + "client_2.session_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Terminal Action | ``node-send-remote-command``\n", + "\n", + "The yaml snippet below shows all the relevant agent options for this action:\n", + "\n", + "```yaml\n", + "\n", + " action_space:\n", + " action_list:\n", + " ...\n", + " - type: node-send-remote-command\n", + " ...\n", + " options:\n", + " nodes: # Node List\n", + " - node_name: client_1\n", + " ...\n", + " ...\n", + " action_map:\n", + " 1:\n", + " action: node-send-remote-command\n", + " options:\n", + " node_id: 0 # Index 0 at the node list.\n", + " remote_ip: 192.168.10.22\n", + " commands:\n", + " - file_system\n", + " - create\n", + " - file\n", + " - downloads\n", + " - cat.png\n", + " - False\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(3)\n", + "client_2.file_system.show(full=True)" + ] } ], "metadata": { diff --git a/src/primaite/notebooks/UC7-E2E-Demo.ipynb b/src/primaite/notebooks/UC7-E2E-Demo.ipynb new file mode 100644 index 00000000..7baac66c --- /dev/null +++ b/src/primaite/notebooks/UC7-E2E-Demo.ipynb @@ -0,0 +1,1621 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# UC7 Demonstration\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n", + "\n", + "\n", + "UC7 is a cybersecurity scenario set in a generic medium sized organisation, where multiple LAN networks which interconnect via the 'internet' to represent a corporate WAN. Comprising of four major networks; `Home Office (HOME)`, `INTERNET (ISP)`, `REMOTE SITE (REMOTE)` and the larger main site `SOME_TECH`. Each network is comprised of routers, switches, computers and servers which green agents use to represent a more real-world accurate pattern of life.\n", + "\n", + "Intended to be a step-up from the [smaller network of UC2](./Data-Manipulation-E2E-Demonstration.ipynb), UC7 introduces two new potential attacks (TAPS) that the blue agent must defend against. \n", + "\n", + "_This notebook acts as the starting point for any users unfamiliar with UC7 and will also sign post to any other UC7 relevant notebooks for further information._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.system.services.dns.dns_server import DNSServer\n", + "from primaite.simulator.system.software import SoftwareHealthState\n", + "from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus\n", + "from primaite.simulator.network.hardware.nodes.network.switch import Switch\n", + "from primaite.simulator.system.applications.web_browser import WebBrowser\n", + "from primaite.simulator.network.container import Network\n", + "from primaite.simulator.system.services.service import ServiceOperatingState\n", + "from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState\n", + "from primaite.simulator.system.services.database.database_service import DatabaseService\n", + "from primaite.simulator.system.applications.database_client import DatabaseClient\n", + "from primaite.simulator.network.hardware.nodes.network.firewall import Firewall\n", + "from primaite.game.game import PrimaiteGame\n", + "from primaite.simulator.sim_container import Simulation\n", + "import yaml\n", + "from pprint import pprint\n", + "from primaite.config.load import load, _EXAMPLE_CFG" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "use_case_7_config = load(_EXAMPLE_CFG/\"uc7_config.yaml\")\n", + "env = PrimaiteGymEnv(env_config=use_case_7_config)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Table of Contents\n", + "\n", + "- [Network Description](#network-description)\n", + " - [Home Office](#network--home-office)\n", + " - [Internet](#network--internet)\n", + " - [Remote Site](#network--remote-site)\n", + " - [ST DMZ](#network--some_tech-dmz)\n", + " - [ST Main Site](#network--some_tech-main-site)\n", + " - [ST Head Office](#network--some_tech-main-site--some_tech-head-office-st_ho)\n", + " - [ST Human Resources](#network--some_tech-main-site--some_tech-human-resources-st_hr)\n", + " - [ST Data](#network--some_tech-main-site--some_tech-data-st_data)\n", + " - [ST Project A](#network--some_tech-main-site--some_tech-project-a-st_proj_a)\n", + " - [ST Project B](#network--some_tech-main-site--some_tech-project-b-st_proj_b)\n", + " - [ST Project C](#network--some_tech-main-site--some_tech-project-c-st_proj_c)\n", + "- [Agents](#agent-description)\n", + " - [Green POL](#agents--green-pattern-of-life-pol)\n", + " - [DatabaseService](#agents--green-pattern-of-life-pol--database-service)\n", + " - [web-server](#agents--green-pattern-of-life-pol--web-server)\n", + " - [Red Agent](#agents--red-agent)\n", + " - [TAP001](#agents--red-agent--threat-actor-profile-001-tap001)\n", + " - [TAP003](#agents--red-agent--threat-actor-profile-003-tap003)\n", + " - [Blue Agent](#agents--blue-agent)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## NETWORK DESCRIPTION\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "uc7_network = env.game.simulation.network\n", + "uc7_network.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "| Default Computer Software |\n", + "|------------------|\n", + "| DNS Client (Service) | \n", + "| NTP Client (Service) | \n", + "| web-browser (Application) |\n", + "| database-client (Application) | " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "| Default Server Software |\n", + "|------------------|\n", + "| DNS Client (Service) | \n", + "| NTP Client (Service) | " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | HOME OFFICE \n", + "\n", + "The HOME OFFICE (HOME) network simulates a generic home office that consist of the following:\n", + "\n", + "- A main LAN with two computers (`HOME-PUB-PC-1`/`2`) and a server (`HOME-PUB-SRV`) using default configurations.\n", + "- A switch (`HOME-PUB-SW-AS`) which connects the above hosts.\n", + "- Lastly, a router (`HOME-PUB-RT-DR`) which connects the home office to the wider networks. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Home Office PC 1 (HOME-PUB-PC-1)\n", + "home_pub_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-PC-1\")\n", + "home_pub_pc_1.show()\n", + "home_pub_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Home Office Router (HOME-PUB-RT-DR)\n", + "home_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-RT-DR\")\n", + "home_pub_rt_dr.show_nic()\n", + "home_pub_rt_dr.show_open_ports()\n", + "home_pub_rt_dr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | INTERNET (ISP)\n", + "\n", + "The internet (ISP) network intends to represent the internet. Currently, it's not feasible or possible for PrimAITE to simulate an internet service provider as well as the internet as a whole to a high degree of fidelity. Thus, in UC7, the \"internet\" refers to the following:\n", + "\n", + "- A router (`ISP-PUB-RT-BR`) which connects the all other UC7 LANS together. \n", + "- A server (`ISP-PUB-SRV-DNS`) which acts as the public DNS server that all PC's use. (Default IP of this host is *8.8.8.8*) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isp_pub_rt_br: Router = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-RT-BR\")\n", + "isp_pub_rt_br.show_nic()\n", + "isp_pub_rt_br.show_open_ports()\n", + "isp_pub_rt_br.acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isp_pub_srv_dns: Server = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-SRV-DNS\")\n", + "isp_pub_srv_dns.show_nic()\n", + "isp_pub_srv_dns_server: DNSServer = isp_pub_srv_dns.software_manager.software[\"dns-server\"]\n", + "isp_pub_srv_dns_server.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | REMOTE SITE \n", + "\n", + "The remote site (`REMOTE`) network is similar to the previously introduced `HOME` network but includes a firewall between the `ISP` and the `REMOTE` network:\n", + "\n", + "- A main LAN with two computers (`REM-PUB-PC-1`/`2`) and a server (`REM-PUB-SRV`) using default configurations.\n", + "- A switch (`REM-PUB-SW-AS`) which connects the above hosts.\n", + "- A router (`HOME-PUB-RT-DR`) which connects the home office to a firewall (`REM-PUB-FW`) which then connects back to the `INTERNET` network.\n", + "\n", + "By default the `REM-PUB-FW` will not block any traffic." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rem_pub_fw: Firewall = uc7_network.get_node_by_hostname(hostname=\"REM-PUB-FW\")\n", + "rem_pub_fw.show_nic()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# By default all of the `REM_PUB_FW` acls are configured to permit all traffic\n", + "rem_pub_fw.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH DMZ (ST DMZ)\n", + "\n", + "The `ST_DMZ` network is a small but important segment which sits between the `INTERNET` network (and thus the `REMOTE` and `HOME` networks as well) and the wider `SOME_TECH` main site networks. Additionally, accessible through the `ST_DMZ` firewall (`ST-PUB-FW`)'s DMZ port is a server which hosts a `web-server` that is accessible to all hosts in the UC7 network." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST DMZ Public Firewall (Permits all traffic by default)\n", + "st_pub_fw: Firewall = uc7_network.get_node_by_hostname(hostname=\"ST-PUB-FW\")\n", + "st_pub_fw.show_nic()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST DMZ Public web-server\n", + "st_dmz_pub_srv_web: Server = uc7_network.get_node_by_hostname(hostname=\"ST-DMZ-PUB-SRV-WEB\")\n", + "st_dmz_pub_srv_web.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE\n", + "\n", + "SOME TECH's main site is comprised of 6 different networks:\n", + "\n", + "| ST Network | Purpose |\n", + "|------------|---------|\n", + "| `ST_HO` | Some Tech's Head Office. |\n", + "| `ST_HR` | Some Tech's Human Resourcing department |\n", + "| `ST_DATA` | Contains the ST database and backup server (FTP) |\n", + "| `ST_PROJ_A` | Development Network |\n", + "| `ST_PROJ_B` | Development Network |\n", + "| `ST_PROJ_C` | Development Network |\n", + "\n", + "In order for hosts to communicate between each network and the wider internet, the main site utilises three routers' `ST-INTRA-PRV-RT-DR-1`, `ST-INTRA-PRV-RT-DR-2` and `ST-INTRA-PRV-RT-CR`.\n", + "\n", + "The `ST-INTRA-PRV-RT-DR-1` router is responsible for routing all traffic from the `ST_PROJ_A`/`B`/`C` networks whereas the `ST-INTRA-PRV-RT-DR-2` router routes all traffic from the `ST_HO`/`HR` networks. Both of which then forward all traffic to the main `ST-INTRA-PRV-RT-CR` router. \n", + "\n", + "This central router connects to the `ST_DMZ` firewall (`ST-PUB-FW`) as well as any traffic that is headed to the `ST_DATA` (the ST database and database storage) network.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr: Router = uc7_network.get_node_by_hostname(hostname=\"ST-INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_cr.route_table.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_dr_1: Router = uc7_network.get_node_by_hostname(hostname=\"ST-INTRA-PRV-RT-DR-1\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_dr_2: Router = uc7_network.get_node_by_hostname(hostname=\"ST-INTRA-PRV-RT-DR-2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH HEAD OFFICE (ST_HO)\n", + "\n", + "The some tech head office (`ST_HO`) is a simple LAN containing three computers with the default PC configuration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_head_office_private_pc_1: Computer = uc7_network.get_node_by_hostname(\"ST-HO-PRV-PC-1\")\n", + "st_head_office_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH HUMAN RESOURCES (ST_HR)\n", + "\n", + "Similarly, the some tech head human resources office (`ST_HR`) consisting of three default PC configurations computers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_human_resources_private_pc_2: Computer = uc7_network.get_node_by_hostname(\"ST-HR-PRV-PC-2\")\n", + "st_human_resources_private_pc_2.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH DATA (ST_DATA)\n", + "\n", + "The `ST_DATA` networks contains two servers pivotal to the daily operation of SOME_TECH.\n", + "\n", + "| Server | Purpose |\n", + "|--------|---------|\n", + "| `ST-DATA-PRV-SRV-DB` | Hosts the `database-service` that all `database-client` are configured to use. | \n", + "| `ST-DATA-PRV-SRV-STORAGE`| Acts as a storage server for the `ST-DATA-PRV-SRV-DB`. |" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_data_private_server_database: Server = uc7_network.get_node_by_hostname(\"ST-DATA-PRV-SRV-DB\")\n", + "st_data_private_server_database_service: DatabaseService = st_data_private_server_database.software_manager.software[\"database-service\"]\n", + "st_data_private_server_database.software_manager.show()\n", + "st_data_private_server_database.software_manager.file_system.show(full=True)\n", + "st_data_private_server_database_service.backup_server_ip" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_data_private_server_storage: Server = uc7_network.get_node_by_hostname(\"ST-DATA-PRV-SRV-STORAGE\")\n", + "st_data_private_server_storage.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH PROJECT A (ST_PROJ_A)\n", + "\n", + "All of the `ST_PROJ_A`/`B`/`C` project networks contain three computers and a switch which connects to the `ST_INTRA-PRV-RT-DR-1` router (as described previously)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1: Computer = uc7_network.get_node_by_hostname(\"ST-PROJ-A-PRV-PC-1\")\n", + "st_project_a_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH PROJECT B (ST_PROJ_B)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_b_private_pc_2: Computer = uc7_network.get_node_by_hostname(\"ST-PROJ-B-PRV-PC-2\")\n", + "st_project_b_private_pc_2.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH PROJECT C (ST_PROJ_C)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_c_private_pc_3: Computer = uc7_network.get_node_by_hostname(\"ST-PROJ-C-PRV-PC-3\")\n", + "st_project_c_private_pc_3.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use Case 7 Agent Description / Demonstration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### AGENTS | Green Pattern of Life (*PoL*)\n", + "\n", + "The UC7 green pattern of life refers to the traffic and behaviour generated by the default `32` different green agents. For each host on the UC7 network, two green agents will attempt to execute the `web-browser` and `database-client` applications and receive a positive or negative reward dependant on whenever they are successful or not.\n", + "\n", + "The table below gives a summary of the different green agent behaviour split between the different UC7 networks.\n", + "\n", + "| **Network** |**Agent Host** | **Application** | **Behaviour Type** | **Probabilities** | **Start Step** | **Start Variance** | **Frequency** | **Variance** | **Reward Impact** |\n", + "|:-----------:|:-------:|:---------------:|:------------------:|:-----------------:|:--------------:|:------------------:|:-------------:|:------------:|:------------------:|\n", + "| `HOME SITE` |`HOME-PUB-PC-1`| `database-client`| *Periodic* | |**4** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `HOME SITE` |`HOME-PUB-PC-2`| `database-client`| *Periodic* | |**8** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `HOME SITE` |`HOME-PUB-PC-1`/`2`| `web-browser` | *Probabilistic* |**20%** | | | | |**LOW** |\n", + "| `REMOTE SITE` |`REM-PUB-PC-1`| `database-client`| *Periodic* | |**12** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `REMOTE SITE` |`REM-PUB-PC-2`| `database-client`| *Periodic* | |**16** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `REMOTE SITE` |`REM-PUB-PC-1`/`2`| `web-browser` | *Probabilistic* |**20%** | | | | |**LOW** |\n", + "| `ST PROJECT A`/`B`/`C` |`ST-PROJ-*-PRV-PC-1`| `database-client`| *Periodic* | |**1** |**1** |**4** |**1** |**HIGH** |\n", + "| `ST PROJECT A`/`B`/`C` |`ST-PROJ-*-PRV-PC-1`| `web-browser` | *Probabilistic* |**40%** | | | | |**LOW** |\n", + "| `ST PROJECT A`/`B`/`C` |`ST-PROJ-*-PRV-PC-2`/`3`| `database-client`| *Periodic* | |**1** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `ST PROJECT A`/`B`/`C` |`ST-PROJ-*-PRV-PC-2`/`3`| `web-browser` | *Probabilistic* |**20%** | | | | |**LOW** |\n", + "| `ST HEAD OFFICE` |`ST-HO-PRV-PC-1`| `web-browser` | *Probabilistic* |**60%** | | | | |**HIGH** |\n", + "| `ST HEAD OFFICE` |`ST-HO-PRV-PC-2`/`3`| `web-browser` | *Probabilistic* |**60%** | | | | |**MEDIUM** |\n", + "| `ST HUMAN RESOURCES` |`ST-HR-PRV-PC-1`| `web-browser` | *Probabilistic* |**60%** | | | | |**MEDIUM** |\n", + "| `ST HUMAN RESOURCES` |`ST-HR-PRV-PC-2`/`3`| `web-browser` | *Probabilistic* |**60%** | | | | |**LOW** |\n", + "\n", + "\n", + "For the full details on each green agent then please click on the drop-down menu below:\n", + "\n", + "
\n", + " UC7 Green Agent Full Details\n", + "\n", + " **ID** | **PoL Type** | **Description of Activity** | **Agent Name** | **Source Node** | **Source App / Service** | **Destination Node** | **Destination App / Service** | **Transport Protocol** | **Application Protocol** | **Behaviour Type** | **Probabilities** | **Start Step** | **Start Variance** | **Max Executions** | **Frequency** | **Variance** | **Reward Impact** \n", + ":------:|:------------:|:---------------------------------------------------:|:-------------------:|:------------------:|:------------------------:|:-----------------------:|:-----------------------------:|:----------------------:|:------------------------:|:------------------:|:-----------------:|:--------------:|:------------------:|:------------------:|:-------------:|:------------:|:-----------------:\n", + " 1 | AGENT | Home Worker accessing Some Tech database | HOME_WORKER-1 | HOME-PUB-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 4 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 2 | AGENT | Home Worker accessing Some Tech web pages | HOME_WORKER-1 | HOME-PUB-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 3 | AGENT | Home Worker accessing Some Tech database | HOME_WORKER-2 | HOME-PUB-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 8 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 4 | AGENT | Home Worker accessing Some Tech web pages | HOME_WORKER-2 | HOME-PUB-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 5 | AGENT | Remote Worker accessing Some Tech database | REMOTE_WORKER-1 | REM-PUB-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 12 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 6 | AGENT | Remote Worker accessing Some Tech web pages | REMOTE_WORKER-1 | REM-PUB-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 7 | AGENT | Remote Worker accessing Some Tech database | REMOTE_WORKER-2 | REM-PUB-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 16 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 8 | AGENT | Remote Worker accessing Some Tech web pages | REMOTE_WORKER-2 | REM-PUB-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 9 | AGENT | Senior Developer accessing Some Tech database | PROJ_A-SENIOR_DEV | ST_PROJ_A-PRV-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | HIGH \n", + " 10 | AGENT | Senior Developer accessing Some Tech web pages | PROJ_A-SENIOR_DEV | ST_PROJ_A-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 40% chance | | | | | | LOW \n", + " 11 | AGENT | Junior Developer accessing Some Tech database | PROJ_A-JUNIOR_DEV-1 | ST_PROJ_A-PRV-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 12 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_A-JUNIOR_DEV-1 | ST_PROJ_A-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 13 | AGENT | Junior Developer accessing Some Tech database | PROJ_A-JUNIOR_DEV-2 | ST_PROJ_A-PRV-PC-3 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 14 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_A-JUNIOR_DEV-2 | ST_PROJ_A-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 15 | AGENT | Senior Developer accessing Some Tech database | PROJ_B-SENIOR_DEV | ST_PROJ_B-PRV-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | HIGH \n", + " 16 | AGENT | Senior Developer accessing Some Tech web pages | PROJ_B-SENIOR_DEV | ST_PROJ_B-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 40% chance | | | | | | LOW \n", + " 17 | AGENT | Junior Developer accessing Some Tech database | PROJ_B-JUNIOR_DEV-1 | ST_PROJ_B-PRV-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 18 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_B-JUNIOR_DEV-1 | ST_PROJ_B-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 19 | AGENT | Junior Developer accessing Some Tech database | PROJ_B-JUNIOR_DEV-2 | ST_PROJ_B-PRV-PC-3 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 20 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_B-JUNIOR_DEV-2 | ST_PROJ_B-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 21 | AGENT | Senior Developer accessing Some Tech database | PROJ_C-SENIOR_DEV | ST_PROJ_C-PRV-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | HIGH \n", + " 22 | AGENT | Senior Developer accessing Some Tech web pages | PROJ_C-SENIOR_DEV | ST_PROJ_C-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 40% chance | | | | | | LOW \n", + " 23 | AGENT | Junior Developer accessing Some Tech database | PROJ_C-JUNIOR_DEV-1 | ST_PROJ_C-PRV-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 24 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_C-JUNIOR_DEV-1 | ST_PROJ_C-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 25 | AGENT | Junior Developer accessing Some Tech database | PROJ_C-JUNIOR_DEV-2 | ST_PROJ_C-PRV-PC-3 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 26 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_C-JUNIOR_DEV-2 | ST_PROJ_C-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 27 | AGENT | CEO accessing Some Tech web pages | CEO | ST_HO-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | HIGH \n", + " 28 | AGENT | CTO accessing Some Tech web pages | CTO | ST_HO-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | MEDIUM \n", + " 29 | AGENT | CFO accessing Some Tech web pages | CFO | ST_HO-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | MEDIUM \n", + " 30 | AGENT | Senior HR accessing Some Tech web pages | SENIOR_HR | ST_HR-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | MEDIUM \n", + " 31 | AGENT | Junior HR accessing Some Tech web pages | JUNIOR_HR-1 | ST_HR-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | LOW \n", + " 32 | AGENT | Junior HR accessing Some Tech web pages | JUNIOR_HR-2 | ST_HR-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### AGENTS | Green *PoL* | database-client Agents Demo\n", + "\n", + "The `database-client` green agents will attempt to use their host's `database-client` application to make a simple connection to the `database-service` on the `ST-DATA-PRV-SRV-DB` host (these connections have no direct impact to the `database-service` or the `database.db` file itself).\n", + "\n", + "Additionally, `database-client` green agents are *Periodic* meaning they will attempt to use the database based on game time-steps. Specifically, these agents will begin on the time-step given in their `start step` setting and will then will reattempt on each subsequence timestep based on the `Frequency` setting. These settings are then randomised using the remaining `Start Variance` and `Variance` options (also given in timesteps). These values are used to *±* their respective base settings to ensure the green agents achieve a moderate amount of domain randomisation in each PrimAITE episode.\n", + "\n", + "For example, take a *Periodic* green agent set with a `start_step` of 4 and a `frequency` of **4** with a `start_variance` and a `variance` of **4** will cause a green agent to make it's first action on timestep $4 \\pm 1$ and then any subsequent actions every $4 \\pm 1$ timesteps afterwards.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset() # Resetting the simulation\n", + "home_pub_pc_1_database_green_agent = env.game.agents.get(\"HOME_WORKER-1-DB\")\n", + "for time_step in range(10):\n", + " env.step(0)\n", + " if not home_pub_pc_1_database_green_agent.history[time_step].action == 'DONOTHING':\n", + " print(home_pub_pc_1_database_green_agent.history[time_step])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_pc_1.sys_log.show(last_n=10)\n", + "st_data_private_server_database.sys_log.show(last_n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### AGENTS | Green *PoL* | web-browser Agents Demo\n", + "\n", + "Unlike the `database-client` green agents, the `web-browser` green agents are *probabilistic*. These agents are quite simple; on every timestep a probability roll is made to determine whenever the agent acts. On a successful outcome the agent will attempt to execute the `web-browser` application which will then attempt to connect to the `ST-DMZ-PUB-SRV-WEB` host (Some Tech's web-server). On a unsuccessful outcome then the green agent will simply perform not action on this timestep.\n", + "\n", + "For example, a `web-browser` green agent with a `20%` chance has a $\\frac{1}{5}$ chance of actioning it's host's `web-browser` to access the `ST-DMZ-PUB-SRV-WEB` web-server. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset() # Resetting the simulation\n", + "home_pub_pc_1_web_browser_green_agent = env.game.agents.get(\"HOME_WORKER-1-WEB\")\n", + "for time_step in range(10):\n", + " env.step(0)\n", + " if not home_pub_pc_1_web_browser_green_agent.history[time_step].action == 'DONOTHING':\n", + " print(home_pub_pc_1_web_browser_green_agent.history[time_step])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-PC-1\")\n", + "home_pub_pc_1_web_browser_green_agent.logger.show()\n", + "home_pub_pc_1_web_browser: WebBrowser = home_pub_pc_1.software_manager.software[\"web-browser\"]\n", + "home_pub_pc_1_web_browser.sys_log.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### AGENTS | Red Agent\n", + "\n", + "For UC7, two new red agents have been developed which introduce a much more complex and realistic attacks in comparison to UC2's [data manipulation red agent](./Data-Manipulation-Customising-Red-Agent.ipynb) for the blue agent to defend against. These new red agents, or more commonly referred to `Threat Actor Profiles` (_TAPS_), utilise a series of different green, blue and red actions to simulate the different steps of a real-world attack.\n", + "\n", + "This notebook does not cover the red agents in much detail, hence it is highly recommended that readers should check out the respective TAP notebooks for a much more in-depth look at each TAP and their impacts.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### AGENTS | RED AGENT | Threat Actor Profile 001 (TAP001)\n", + "\n", + "This TAP aims to exfiltrate and then encrypt the `database.db` file on `ST-DATA-PRV-SRV-DB` host, whilst leaving the functionality of the database intact. Configured by default to start on the `ST-PROJ-A-PRV-PC-1` host, `TAP001` must first embed itself on the host, locate the target (`ST-DATA-PRV-SRV-DB`) through a series of NMAP scans, establish a connection to it's `C2Server` (`ISP-PUB-SRV-DNS` by default) and then finally attempt to exfiltrate and encrypt. \n", + "\n", + "If successful, the blue agent is configured to receive a serve negative reward and thus must prevent `TAP001` from ever reaching the target database. This could be through blocking it's connection to the target or it's `C2Server` via a carefully crafted ACL or perhaps through more a forceful approach such as shutting down the starting host.\n", + "\n", + "For more information on `TAP001` and it's impacts, [please refer to the TAP001 E2E notebook](./UC7-TAP001-Kill-Chain-E2E.ipynb) or for more blue agent involved demonstration refer to the [UC7 attack variants notebook](./UC7-attack-variants.ipynb) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# By default the `uc7_config.yaml` is setup to use TAP001\n", + "env.reset()\n", + "for _ in range(128):\n", + " env.step(action=0)\n", + "\n", + "uc7_tap001 = env.game.agents.get(\"attacker\")\n", + "uc7_tap001.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# TAP001 starting host\n", + "st_project_a_private_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"ST-PROJ-A-PRV-PC-1\")\n", + "st_project_a_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1.file_system.show(full=True)\n", + "isp_pub_srv_dns: Server = env.game.simulation.network.get_node_by_hostname(hostname=\"ISP-PUB-SRV-DNS\")\n", + "isp_pub_srv_dns.file_system.show(full=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Database Impact \n", + "st_data_private_server_database: Server = env.game.simulation.network.get_node_by_hostname(hostname=\"ST-DATA-PRV-SRV-DB\")\n", + "st_data_private_server_database.file_system.show(full=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### AGENTS | RED AGENT | Threat Actor Profile 003 (TAP003)\n", + "\n", + "Unlike `TAP001` more traditional representation of a threat actor, `TAP003` represents a malicious insider which leverages it's pre-existing knowledge to covertly add malicious access control lists (ACLs) to three different routers each of which affecting green agent traffic in a different way causing the blue agent to receive negative rewards. Thus, the blue agent must learn to leverage it's ability to remove rules and change credentials throughout the network to rectify the impacts of `TA003` and re-establish green POL and prevent TAP003 from accessing additional routers.\n", + "\n", + "The table below is a brief summary of the malicious acls added by `TAP003`\n", + "\n", + "|Target Router | Impact |\n", + "|----------------------|--------|\n", + "|`ST-INTRA-PRV-RT-DR-1`| Blocks all `POSTGRES_SERVER` that arrives at the `ST-INTRA-PRV-RT-DR-1` router. This rule will prevent all ST_PROJ_* hosts from accessing the database (`ST-DATA-PRV-SRV-DB`).|\n", + "|`ST-INTRA-PRV-RT-CR`| Blocks all `HTTP` traffic that arrives at the`ST-INTRA-PRV-RT-CR` router. This rule will prevent all SOME_TECH hosts from accessing the web-server (`ST-DMZ-PUB-SRV-WEB`)|\n", + "|`REM-PUB-RT-DR`| Blocks all `DNS` traffic that arrives at the `REM-PUB-RT-DR` router. This rule prevents any remote site works from accessing the DNS Server (`ISP-PUB-SRV-DNS`).|\n", + "\n", + "Lastly, it's highly recommended that users refer to the [TAP003 E2E notebook](./UC7-TAP003-Kill-Chain-E2E.ipynb) for further information or for the [UC7 attack variants notebook](./UC7-attack-variants.ipynb) demonstration of TAP003 defence." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Loading up the TAP003 UC7 config variant\n", + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['io_settings']['save_agent_actions'] = True # Saving syslogs\n", + "env = PrimaiteGymEnv(env_config=cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# By default the `uc7_config.yaml` is setup to use TAP001\n", + "env.reset()\n", + "for _ in range(128):\n", + " env.step(action=0)\n", + "\n", + "uc7_tap003 = env.game.agents.get(\"attacker\")\n", + "uc7_tap003.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-DR-1\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-CR\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\").acl.show() " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## AGENTS | Blue Agent\n", + "\n", + "\n", + "\n", + "\n", + "In PrimAITE blue agent configuration is split into three separate sections, each responsible for a different part of the blue agent functionality.\n", + "\n", + "- `observation_space`\n", + "\n", + "The observation_space (or more commonly `OBS`) refers to the what simulation components the blue agent observes each `time_step`. A blue agent `OBS` can be configured to be as large the entire network or just an individual node. \n", + "\n", + "- `action_space`\n", + "\n", + "The action space configuration is used to set the actions available to the blue agent. The larger range of actions could be optimal for responding to a large set of different scenarios but could lead to longer training times. Crafting the perfect `action_space` is pivotal for creating an effective blue agent.\n", + "\n", + "- `reward_function`\n", + "\n", + "The `reward_function` section configures what type of reward (or penalisation) the blue agent will receive based on the current status of the simulation. A balanced and well crafted reward function is the path of success for any blue agent to learn what the best approach to scenario may be.\n", + "\n", + "\n", + "\n", + "_The remaining section of this notebook will cover the UC7 blue agent and the default aforementioned settings._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset() # Resetting the env\n", + "defender = env.game.agents.get(\"defender\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## AGENTS | Blue Agent | Observation Space (OBS)\n", + "\n", + "## NETWORK DESCRIPTION\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "_(Click to enlarge)_\n", + "\n", + "Represented as a hierarchy (i.e the same way as a dictionary) the blue agent OBS is seperate into multiple sections.\n", + "\n", + "### Links\n", + "\n", + "The first section is Links, which is used to report the current network traffic load on every link in the network. Since the observation space is composed of categorical variables, the traffic values are converted from MBit/s to a category based on a percentage of the link's capacity.\n", + "\n", + "
Link Values Mapping\n", + "\n", + "|load observation|percent utilisation|\n", + "|--|--|\n", + "|0|exactly 0%|\n", + "|1|0-11%|\n", + "|2|11-22%|\n", + "|3|22-33%|\n", + "|4|33-44%|\n", + "|5|44-55%|\n", + "|6|55-66%|\n", + "|7|66-77%|\n", + "|8|77-88%|\n", + "|9|88-99%|\n", + "|10|exactly 100%|\n", + "\n", + "\n", + "
\n", + "\n", + "
\n", + " Link List\n", + " \n", + "|Link number|Endpoint A|Endpoint B|\n", + "|---|---|---|\n", + "|1|HOME-PUB-SW-AS:eth-1|HOME-PUB-RT-DR:eth-1 |\n", + "|2|HOME-PUB-SW-AS:eth-1|HOME-PUB-PC-1:eth-1 |\n", + "|3|HOME-PUB-SW-AS:eth-1|HOME-PUB-PC-2:eth-1 |\n", + "|4|HOME-PUB-SW-AS:eth-1|HOME-PUB-SRV:eth-1 | \n", + "|5|ISP-PUB-RT-BR:eth-1|HOME-PUB-RT-DR:eth-2 |\n", + "|6|ISP-PUB-RT-BR:eth-2|ISP-PUB-SRV-DNS:eth-1 |\n", + "|7|ISP-PUB-RT-BR:eth-3|REM-PUB-FW:eth-1 |\n", + "|8|REM-PUB-FW:eth-2|REM-PUB-RT-DR:eth-1 |\n", + "|9|REM-PUB-RT-DR:eth-2|REM-PUB-SW-AS:eth-1 |\n", + "|10|REM-PUB-SW-AS:eth-2|REM-PUB-PC-1:eth-1 |\n", + "|11|REM-PUB-SW-AS:eth-3|REM-PUB-PC-2:eth-1 | \n", + "|12|REM-PUB-SW-AS:eth-4|REM-PUB-SRV:eth-1 |\n", + "|13|ISP-PUB-RT-BR:eth-4|ST-PUB-FW:eth-1 |\n", + "|14|ST-PUB-FW:eth-3|ST-DMZ-PUB-SRV-WEB:eth-1 |\n", + "|15|ST-INTRA-PRV-RT-CR:eth-1|ST-PUB-FW:eth-2 |\n", + "|16|ST-INTRA-PRV-RT-CR:eth-2|ST-INTRA-PRV-RT-DR-1:eth-1 |\n", + "|17|ST-INTRA-PRV-RT-CR:eth-3|ST-INTRA-PRV-RT-DR-2:eth-1 | \n", + "|18|ST-INTRA-PRV-RT-CR:eth-4|ST-DATA-PRV-SW-AS:eth-1 |\n", + "|19|ST-HO-PRV-SW-AS:eth-1|ST-INTRA-PRV-RT-DR-2:eth-2 |\n", + "|20|ST-HO-PRV-SW-AS:eth-2|ST-HO-PRV-PC-1:eth-1 |\n", + "|21|ST-HO-PRV-SW-AS:eth-3|ST-HO-PRV-PC-2:eth-1 |\n", + "|22|ST-HO-PRV-SW-AS:eth-4|ST-HO-PRV-PC-3:eth-1 |\n", + "|23|ST-HR-PRV-SW-AS:eth-1|ST-INTRA-PRV-RT-DR-2:eth-3 |\n", + "|24|ST-HR-PRV-SW-AS:eth-2|ST-HR-PRV-PC-1:eth-1 |\n", + "|25|ST-HR-PRV-SW-AS:eth-3|ST-HR-PRV-PC-2:eth-1 |\n", + "|26|ST-HR-PRV-SW-AS:eth-4|ST-HR-PRV-PC-3:eth-1 |\n", + "|27|ST-DATA-PRV-SW-AS:eth-2|ST-DATA-PRV-SRV-STORAGE:eth-1 |\n", + "|28|ST-DATA-PRV-SW-AS:eth-3|ST-DATA-PRV-SRV-DB:eth-1 | \n", + "|29|ST-INTRA-PRV-RT-DR-1:eth-2|ST-PROJ-A-PRV-SW-AS:eth-1 |\n", + "|30|ST-PROJ-A-PRV-SW-AS:eth2|ST-PROJ-A-PRV-PC-1:eth-1|\n", + "|31|ST-PROJ-A-PRV-SW-AS:eth3|ST-PROJ-A-PRV-PC-2:eth-1 |\n", + "|32|ST-PROJ-A-PRV-SW-AS:eth4|ST-PROJ-A-PRV-PC-3:eth-1 | \n", + "|33|ST-INTRA-PRV-RT-DR-1:eth-3|ST-PROJ-B-PRV-SW-AS:eth-1 |\n", + "|34|ST-PROJ-B-PRV-SW-AS:eth2|ST-PROJ-B-PRV-PC-1:eth-1 |\n", + "|35|ST-PROJ-B-PRV-SW-AS:eth3|ST-PROJ-B-PRV-PC-2:eth-1 |\n", + "|36|ST-PROJ-B-PRV-SW-AS:eth4|ST-PROJ-B-PRV-PC-3:eth-1 | \n", + "|37|ST-INTRA-PRV-RT-DR-1:eth-4|ST-PROJ-C-PRV-SW-AS:eth-1 |\n", + "|38|ST-PROJ-A-PRV-SW-AS:eth2|ST-PROJ-C-PRV-PC-1:eth-1 |\n", + "|39|ST-PROJ-A-PRV-SW-AS:eth3|ST-PROJ-C-PRV-PC-2:eth-1 |\n", + "|40|ST-PROJ-A-PRV-SW-AS:eth4|ST-PROJ-C-PRV-PC-3:eth-1 |\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below prints out how the above link list table appears in the blue agent observation space" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "\n", + "obs, reward, _,_,info = env.step(0)\n", + "for i,x in obs['LINKS'].items():\n", + " print(i, x)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### HOSTS\n", + "\n", + "By default the blue agent is monitoring `3` different computers and `1` server:\n", + "\n", + "|Host label|hostname| services | applications | folders | files |\n", + "|:--:|:--:|:--:|:--:|:--:|:--:|\n", + "|HOST1|ST-PROJ-A-PRV-PC-1 | ftp-client | RansomwareScript | downloads, exfiltration_folder | malware_dropper.ps1, database.db |\n", + "|HOST2|ST-PROJ-B-PRV-PC-2| ftp-client | RansomwareScript | downloads, exfiltration_folder | malware_dropper.ps1, database.db |\n", + "|HOST3|ST-PROJ-C-PRV-PC-3| ftp-client | RansomwareScript | downloads, exfiltration_folder | malware_dropper.ps1, database.db |\n", + "|HOST4|ST-DATA-PRV-SRV-DB||| database | database.db|\n", + "\n", + "\n", + "Each `time_step` these hosts report the following to the blue agent:\n", + "\n", + "- operating status \n", + "- number of file creations\n", + "- number of file deletions\n", + "- up to 4 installed services (operating status and health status)\n", + "- up to 2 installed applications (operating status, health status, and number of executions)\n", + "- up to 1 folder (health status)\n", + "- up to 1 file on the folders (health status and number of accesses)\n", + "- 1 network interface, including: \n", + " - operating status\n", + " - number of malicious network events detected\n", + " - amount of incoming and outgoing traffic on each protocol and port\n", + "\n", + "\n", + "\n", + "It's worth noting that for larger observation spaces, not every host will have the maximum number of files, folders, services, and applications configured for the observation space, so padding is used to ensure that the shape of each host observation is the same. The hosts appear in the order that they are defined in the config file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "\n", + "obs, reward, _,_,info = env.step(0)\n", + "for node_id, node_obs in obs['NODES'].items():\n", + " if not \"ROUTER\" in node_id: # filter out router OBS for now\n", + " print(node_id)\n", + " pprint(node_obs)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For any readers unfamiliar with the different enumeration values in any of the obs output (i.e what the `0`,`1`,`2`,`3` values represent) then please refer to the following tables for reference:\n", + "\n", + "
Host operating state category table\n", + "\n", + "|value|meaning|\n", + "|-|-|\n", + "|0|UNUSED|\n", + "|1|ON|\n", + "|2|OFF|\n", + "|3|BOOTING|\n", + "|4|SHUTTING DOWN|\n", + "\n", + "
\n", + "\n", + "
No. file creations/deletions category table\n", + "\n", + "|value|meaning|\n", + "|-|-|\n", + "|0|0|\n", + "|1|1-5|\n", + "|2|6-10|\n", + "|3|>10|\n", + "\n", + "
\n", + "\n", + "
Service operating state category table\n", + "\n", + "|operating_state|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|RUNNING|\n", + "|2|STOPPED|\n", + "|3|PAUSED|\n", + "|4|DISABLED|\n", + "|5|INSTALLING|\n", + "|6|RESTARTING|\n", + "\n", + "
\n", + "\n", + "
Application operating state category table\n", + "\n", + "|operating_state|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|RUNNING|\n", + "|2|CLOSED|\n", + "|3|INSTALLING|\n", + "\n", + "
\n", + "\n", + "
Service/Application health state category table\n", + "\n", + "|health_state|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|GOOD|\n", + "|2|FIXING|\n", + "|3|COMPROMISED|\n", + "|4|OVERWHELMED|\n", + "\n", + "
\n", + "\n", + "
Application number of executions category table\n", + "\n", + "|num_executions label|meaning|\n", + "|-|-|\n", + "|0|0|\n", + "|1|1-5|\n", + "|2|6-10|\n", + "|3|>10|\n", + "\n", + "
\n", + "\n", + "
Folder/file health status table\n", + "\n", + "|health_status label|meaning|\n", + "|-|-|\n", + "|0|UNUSED|\n", + "|1|GOOD|\n", + "|2|COMPROMISED|\n", + "|3|CORRUPT|\n", + "|4|RESTORING|\n", + "|5|REPAIRING|\n", + "\n", + "
\n", + "\n", + "
File number of access category table\n", + "\n", + "|num_access label|meaning|\n", + "|-|-|\n", + "|0|0|\n", + "|1|1-5|\n", + "|2|6-10|\n", + "|3|>10|\n", + "\n", + "
\n", + "\n", + "
NICs' operating_status category table\n", + "\n", + "|operating_status|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|ENABLED|\n", + "|2|DISABLED|\n", + "\n", + "
\n", + "\n", + "
NIC monitored traffic utilisation category table \n", + "\n", + "|load observation|percent utilisation|\n", + "|--|--|\n", + "|0|exactly 0%|\n", + "|1|0-11%|\n", + "|2|11-22%|\n", + "|3|22-33%|\n", + "|4|33-44%|\n", + "|5|44-55%|\n", + "|6|55-66%|\n", + "|7|66-77%|\n", + "|8|77-88%|\n", + "|9|88-99%|\n", + "|10|exactly 100%|\n", + "\n", + "\n", + "
\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Routers and Firewalls\n", + "\n", + "In addition, the agent can observe the list of Access Control List rules present on routers and firewalls.\n", + "\n", + "Routers have one ACL and a list of network interfaces (ports)\n", + "\n", + "Firewalls have six ACLs and three ports (ports are Internal/External/DMZ, with an Inbound and Outbound ACL for each)\n", + "\n", + "by default, the UC7 agent is configured to observe `3` different routers:\n", + "\n", + "- `ST-INTRA-PRV-RT-CR`\n", + "- `ST-INTRA-PRV-RT-DR-1`\n", + "- `REM-PUB-RT-DR`\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Port operating status category table\n", + "\n", + "|operating_status|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|ENABLED|\n", + "|2|DISABLED|\n", + "\n", + "
\n", + "\n", + "
ACL Permission category table\n", + "\n", + "|permission|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|ALLOW|\n", + "|2|DENY|\n", + "\n", + "
\n", + "\n", + "\n", + "
ACL source/destination IP ID category table\n", + "\n", + "|IP ID|IP address| Associated node|\n", + "|--|--|--|\n", + "|0|UNUSED|\n", + "|1|ALL IPs|\n", + "|2|192.168.1.2 | HOME-PUB-PC-1 |\n", + "|3|192.168.1.3 | HOME-PUB-PC-2 |\n", + "|4|192.168.1.4 | HOME-PUB-PC-SRV |\n", + "|5|192.168.20.2 | REM-PUB-PC-1 |\n", + "|6|192.168.20.3 | REM-PUB-PC-2 |\n", + "|7|192.168.20.4 | REM-PUB-SRV |\n", + "|8|192.168.100.2| ST_PUB_SRV_WEB_IP |\n", + "|9|192.168.200.2 | ST-HO-PRV-PC-1 |\n", + "|10|192.168.200.3 | ST-HO-PRV-PC-2 |\n", + "|11|192.168.200.4 | ST-HO-PRV-PC-3 |\n", + "|12|192.168.210.2 | ST-HR-PRV-PC-1 |\n", + "|13|192.168.210.3 | ST-HR-PRV-PC-2 |\n", + "|14|192.168.210.4 | ST-HR-PRV-PC-3 |\n", + "|15|192.168.220.2 | ST-DATA-PRV-SRV-STORAGE | \n", + "|16|192.168.220.3 | ST-DATA-PRV-SRV-DB |\n", + "|17|192.168.230.2 | PROJ-A-PRV-PC-1 |\n", + "|18|192.168.230.3 | PROJ-A-PRV-PC-2 |\n", + "|19|192.168.230.4 | PROJ-A-PRV-PC-3 |\n", + "|20|192.168.240.2 | PROJ-B-PRV-PC-1 |\n", + "|21|192.168.240.3 | PROJ-B-PRV-PC-2 |\n", + "|22|192.168.240.4 | PROJ-B-PRV-PC-3 |\n", + "|23|192.168.250.2 | PROJ-C-PRV-PC-1 |\n", + "|24|192.168.250.3 | PROJ-C-PRV-PC-2 |\n", + "|25|192.168.250.4 | PROJ-C-PRV-PC-3 |\n", + "\n", + "
\n", + "\n", + "
ACL Rule IP Address Wildcard category table\n", + "\n", + "|wildcard|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|None|\n", + "|2|0.0.0.1|\n", + "|3|0.0.0.255|\n", + "|4|0.0.255.255|\n", + "
\n", + "\n", + "
ACL Rule Port category table\n", + "\n", + "|permission|label|used for|\n", + "|--|--|--|\n", + "|0|UNUSED|padding|\n", + "|1|ANY||\n", + "|2|21|FTP|\n", + "|3|53|DNS|\n", + "|4|80|HTTP|\n", + "|5|123|NTP|\n", + "|6|5432|POSTGRES SERVER|\n", + "|7|22|SSH|\n", + "\n", + "
\n", + "\n", + "
ACL Rule Protocol category table\n", + "\n", + "|permission|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|ANY|\n", + "|2|ICMP|\n", + "|3|TCP|\n", + "|4|UDP|\n", + "\n", + "
\n", + "\n", + "
Firewall ports \n", + "\n", + "|index|label|\n", + "|--|--|\n", + "|1|external|\n", + "|2|internal|\n", + "|3|DMZ|\n", + "\n", + "
\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, _,_,info = env.step(0)\n", + "for node_id, node_obs in obs['NODES'].items():\n", + " if not \"HOST\" in node_id: # filter out hosts OBS and focus on ROUTER\n", + " print(node_id)\n", + " pprint(node_obs)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## AGENTS | Blue Agent | Action Space" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `action_map`\n", + "\n", + "Numerically ordered, the `action_map` combines the options set out previously to define the actual details of what actions and the amount of actions that a blue agent can perform.\n", + "\n", + "For example, the snippet below details the first four actions the the default UC7 blue agent is setup with:\n", + "\n", + "```yaml\n", + " action_space:\n", + " action_list:\n", + " action_map: \n", + " 0:\n", + " action: DONOTHING\n", + " options: {}\n", + "\n", + " # |======================================|\n", + " # | ST-PROJ-A-PRV-PC-1 |\n", + " # |======================================|\n", + "\n", + " # ST-PROJ-A-PRV-PC-1 | NODE_OS_SCAN\n", + " 1:\n", + " action: NODE_OS_SCAN\n", + " options:\n", + " node_id: 0 # ST-PROJ-A-PRV-PC-1\n", + " # ST-PROJ-A-PRV-PC-1 | NODE_SHUTDOWN\n", + " 2:\n", + " action: NODE_SHUTDOWN\n", + " options:\n", + " node_id: 0 # ST-PROJ-A-PRV-PC-1\n", + " # ST-PROJ-A-PRV-PC-1 | NODE_STARTUP\n", + " 3:\n", + " action: NODE_STARTUP\n", + " options:\n", + " node_id: 0 # ST-PROJ-A-PRV-PC-1\n", + "```\n", + "\n", + "Converting the yaml snippet below we end up with the following:\n", + "\n", + "|Action Num | Action Type | Options|\n", + "|:---------:|:-----------:|:------:|\n", + "|0|**DONOTHING**|*n/a*|\n", + "|1|**NODE_OS_SCAN**|*node_id: 0* \n", + "|2|**NODE_SHUTDOWN**|*node_id: 0* \n", + "|3|**NODE_STARTUP**|*node_id: 0* \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`0: DONOTHING`:\n", + "\n", + "The first action, `DONOTHING` is a default standard that all agents are setup to use by default in primAITE. Quite simply this action makes no impact to the simulation - literally does nothing. Although this obviously does not seem all that useful, in practice an agent with a small yet impactful actions (such as adding or removing ACL's rules) may find that performing no action may be better than risking a potentially detrimental one.\n", + "\n", + "Additionally, you may spotted the code snippet below dotted around this notebook and many others.\n", + "\n", + "```py\n", + " env.step(0)\n", + "```\n", + "\n", + "This code snippet is used to step forward in an PrimAITE episode and force the blue agent into performing no action which is very useful for demonstrating default simulation behaviour as well as the different impacts that the green and red agents have upon the environment.\n", + "\n", + "`1: NODE_OS_SCAN`:\n", + "\n", + "The first actual action that the blue agent can perform is scanning action. Throughout PrimAITE there are a variety of different scanning type actions (such as `NODE_OS_SCAN`, `NODE_APPLICATION_SCAN` or `NODE_FILE/FOLDER_SCAN`) which can be used to by the blue agent to gain a deeper understanding of the simulation state. Specifically, these actions will cause the blue agent's observations to update to the \"true\" `HEALTH_STATUS` of a simulation component. For example, if a red agent corrupts and alters the health status of a file, the blue agent's observation space will not reflect this until the agent performs a `NODE_FILE_SCAN` on the newly corrupted file. It's worth noting that blue agents can be configured to see the true `HEALTH_STATUS` of software and files without needing to scan in the yaml. Although this may make it easier for an train and create an effective blue agent it could be seen as reducing the fidelity of the simulation.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "env.step(0)\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "print(defender.history[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`1: NODE_OS_SCAN`:\n", + "\n", + "### TODO: Currently bugged OBS - Return after migration\n", + "\n", + "The first actual action that the blue agent can perform is scanning action. A variety of different scanning type actions (such as `NODE_APPLICATION_SCAN` or `NODE_FILE/FOLDER_SCAN`) which can be used to by the blue agent to gain a deeper understanding of the simulation state. Specifically, these actions will cause the blue agent's observations to update to the \"true\" `HEALTH_STATUS` of a simulation component. The `NODE_OS_SCAN` acts a combined version of all these scan type actions.\n", + "\n", + "For example, if a red agent corrupts and alters the health status of a file, the blue agent's observation space will not reflect this until the agent performs a `NODE_FILE_SCAN` on the newly corrupted file. It's worth noting that blue agents can be configured to see the true `HEALTH_STATUS` of software and files without needing to scan in the yaml. Although this may make it easier for an train and create an effective blue agent it could be seen as reducing the fidelity of the simulation.\n", + "\n", + "The code snippet below demonstrates an example where the blue agent uses the `NODE_OS_SCAN` action to reveal the true health status `ST-PROJ-A-PRV-PC-1`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, term, trunc, info = env.step(0)\n", + "print(f\"ftp-client (Prior Scan) OBS: {obs['NODES']['HOST0']['SERVICES'][1]}\")\n", + "print(f\"database-client (Prior Scan) OBS: {obs['NODES']['HOST0']['APPLICATIONS']}\")\n", + "obs_list = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"ST-PROJ-A-PRV-PC-1\")\n", + "st_project_a_private_pc_1.software_manager.software[\"ftp-client\"].set_health_state(SoftwareHealthState.COMPROMISED)\n", + "st_project_a_private_pc_1.software_manager.software[\"database-client\"].set_health_state(SoftwareHealthState.COMPROMISED)\n", + "st_project_a_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(1)\n", + "for _ in range(10):\n", + " obs, reward, term, trunc, info = env.step(1)\n", + " obs_list.append(obs['NODES']['HOST0']['SERVICES'][1])\n", + " obs_list.append(obs['NODES']['HOST0']['APPLICATIONS'][2])\n", + "\n", + "obs, reward, term, trunc, info = env.step(0)\n", + "print(f\"ftp-client (Post Scan) OBS: {obs['NODES']['HOST0']['SERVICES']}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs['NODES']['HOST0']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`2: NODE_SHUTDOWN`:\n", + "\n", + "The next action available is the `NODE_SHUTDOWN` action. This action quite is quite simple in comparison and literally attempts to shut down the target host given in the `options` settings which is set to `ST-PROJ-A-PRV-PC-1`. Shutting a PC down affects the `operating_status` of the host machine which the following snippets demonstrate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# `1` is equal to 'ON' in this case.\n", + "obs, reward, term, trunc, info = env.step(0)\n", + "print(f\"ST-PROJ-A-PRV-PC-1's (prior shutdown) operating state: {obs['NODES']['HOST0']['operating_status']}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, term, trunc, info = env.step(2)\n", + "for _ in range(3):\n", + " env.step(0) # 3 second shut down time.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, term, trunc, info = env.step(0)\n", + "print(f\"ST-PROJ-A-PRV-PC-1's (post shutdown) operating state: {obs['NODES']['HOST0']['operating_status']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`3: NODE_STARTUP`:\n", + "\n", + "Luckily, the blue agent can use it's third action `NODE_STARTUP` has been configured to bring the `ST-PROJ-A-PRV-PC-1` back up and running. Although, with the a three timestep reboot time.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, term, trunc, info = env.step(3)\n", + "\n", + "for _ in range(3):\n", + " env.step(0) # 3 second reboot time.\n", + " \n", + "obs, reward, term, trunc, info = env.step(0)\n", + "print(f\"ST-PROJ-A-PRV-PC-1's (prior shutdown) operating state: {obs['NODES']['HOST0']['operating_status']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## AGENTS | Blue Agent | Reward Components\n", + "\n", + "In order to add context to the observation, the blue agent can be configured with different reward components. For any readers unfamiliar with PrimAITE rewards then user guide contains a good introduction as this notebook section will only cover the UC7 blue agent setup.\n", + "\n", + "For `UC7`, the blue agent is setup to receive a negative reward when the `database.db` file integrity is affected (i.e enters into a `COMPROMISED` state):\n", + "\n", + "```yaml\n", + " reward_function:\n", + " reward_components:\n", + " - type: DATABASE_FILE_INTEGRITY\n", + " weight: *HIGH_WEIGHT_IMPACT\n", + " options: \n", + " node_hostname: ST-DATA-PRV-SRV-DB \n", + " folder_name: database\n", + " file_name: database.db\n", + "```\n", + "\n", + "The blue agent's remaining reward function is comprised of **32** different ``SHARED_REWARD`` components. These rewards will grant the blue agent a positive or negative reward based on the current reward of the **32** green agents. The next code snippets The code snippets below demonstrate how the blue agent's reward is affected by simulation state." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(len(defender.reward_function.reward_components)):\n", + " try:\n", + " print(f\"Simulation State Reward: {defender.reward_function.reward_components[i][0].location_in_state}\")\n", + " except:\n", + " print(f\"Green Agent Shared Reward: {defender.reward_function.reward_components[i][0].config.agent_name}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In a perfect episode, with no red agent interference, the blue agent can expect the following reward:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"].pop(32) # removing red agent\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.reset()\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "for _ in range(128):\n", + " env.step(0)\n", + "defender.reward_function.total_reward" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The below code snippet shows what you'd expect the defender's total reward to be after TAP001 is left unchallenged:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.reset()\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "for _ in range(128):\n", + " env.step(0)\n", + "print(f\"Successful TAP001 & Blue Agent Reward: {defender.reward_function.total_reward}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next is what you'd expect the defender's total reward to be after TAP003 is left unchallenged:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['io_settings']['save_agent_actions'] = True # Saving syslogs\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.reset()\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "for _ in range(128):\n", + " env.step(0)\n", + "print(f\"Successful TAP003 & Blue Agent Reward: {defender.reward_function.total_reward}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, if we completely block any green traffic and set the database.db to `COMPROMISED` we can simulate the potential worst case situation for the blue agent (based off the default `reward_components`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.reset()\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "\n", + "# Corrupting and Disabling the database-service\n", + "st_data_private_server_database: Server = env.game.simulation.network.get_node_by_hostname(\"ST-DATA-PRV-SRV-DB\")\n", + "st_data_private_server_database_file = st_data_private_server_database.file_system.get_file(folder_name=\"database\", file_name=\"database.db\")\n", + "st_data_private_server_database_file.health_status = FileSystemItemHealthStatus.COMPROMISED\n", + "st_data_private_server_database.software_manager.software[\"database-service\"].operating_state = ServiceOperatingState.DISABLED\n", + "\n", + "# Shutting down the web-server\n", + "\n", + "st_dmz_pub_srv_web: Server = env.game.simulation.network.get_node_by_hostname(\"ST-DMZ-PUB-SRV-WEB\")\n", + "st_dmz_pub_srv_web.software_manager.software[\"web-server\"].operating_state = ServiceOperatingState.DISABLED\n", + "\n", + "# Shutting down the DNSServer\n", + "\n", + "isp_pub_srv_dns_server: Server = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-SRV-DNS\")\n", + "isp_pub_srv_dns_server.software_manager.software[\"dns-server\"].operating_state = ServiceOperatingState.DISABLED\n", + "\n", + "for _ in range(128):\n", + " env.step(0)\n", + "print(f\"Worst Case Episode Blue Agent Reward: {defender.reward_function.total_reward}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-TAP001-Kill-Chain-E2E.ipynb b/src/primaite/notebooks/UC7-TAP001-Kill-Chain-E2E.ipynb new file mode 100644 index 00000000..da7752fc --- /dev/null +++ b/src/primaite/notebooks/UC7-TAP001-Kill-Chain-E2E.ipynb @@ -0,0 +1,1843 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Mobile Malware Kill Chain - Ransomware Script Variant\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n", + "\n", + "**Threat Actor Profile (TAP):** 001
\n", + "**Kill Chain**: Mobile Malware - Ransomware Script Variant\n", + "\n", + "This notebook demonstrates TAP001 on the UC7 network infrastructure. In this scenario, a some_tech employee within the development project network plugs in his personal device. Whilst browsing, they encounter a ransomware virus which moves onto the host machine thus triggering the malware! \n", + "
\n", + "\n", + "This ransomware variant targets the database service's file directly, rather than the disrupting the database service, hence why in this scenario the data service still functions after becoming corrupted.\n", + "
\n", + "\n", + "The red attack intends to introduce realistic impacts to the observation space, such as files created/removed and applications installing mid-episode whilst still providing usable data for agent training." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Importing the necessary PrimAITE libraries\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "import yaml\n", + "from primaite.config.load import load, _EXAMPLE_CFG\n", + "from deepdiff.diff import DeepDiff" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Utility functions\n", + "\n", + "def display_obs_diffs(old, new, step_counter):\n", + " \"\"\"\n", + " Use DeepDiff to extract and display differences in old and new instances of\n", + " the observation space.\n", + "\n", + " :param old: observation space instance.\n", + " :param new: observation space instance.\n", + " :param step_counter: current step counter.\n", + " \"\"\"\n", + " print(\"\\nObservation space differences\")\n", + " print(\"-----------------------------\")\n", + " diff = DeepDiff(old, new)\n", + " print(f\"Step {step_counter}\")\n", + " for d,v in diff.get('values_changed', {}).items():\n", + " print(f\"{d}: {v['old_value']} -> {v['new_value']}\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Sections:**\n", + "1. Notebook Intro\n", + "2. Notebook Setup\n", + "3. Initial States\n", + "4. Attack Trigger\n", + "5. Post Attack States\n", + "6. Configurations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Intro** | **TAP001 - Mobile Malware Kill Chain** \n", + "\n", + "The TAP001 kill chain is comprised of 6 different stages.
The table below describes the impact of the current TAP001 implementation as well as the attack stage order.\n", + "\n", + "_DOWNLOAD_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|1|Download|**Starting Host** HOST:num_file_creations: HOST:FOLDER:FILES:* |The employee encounters a malicious payload through web browsing on his mobile phone, often disguised as legitimate content.|\n", + "\n", + "_INSTALL_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|2|Install | **Starting Host** HOST:FOLDER:FILES:num_access:|The malware is activated, either by the employee opening the file or automatically upon download, initiating its malicious functions.|\n", + "\n", + "_ACTIVATE_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|3|Activate | **Starting Host** HOST:APPLICATION:operating_status|The malware installs itself on the terminal, often seeking to gain persistence by embedding itself into system processes or startup routines.|\n", + "\n", + "_PROPAGATE_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|4|Propagate| **Multiple Hosts** HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:*|The malware attempts to spread to other systems or networks, looking for vulnerable services.|\n", + "\n", + "_COMMAND_AND_CONTROL_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|5|Command and Control|**Starting Host** HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* HOST:APPLICATIONS:APPLICATION:*|The malware establishes a connection to an external command and control (C&C) server, receiving instructions and possibly exfiltrating data.|\n", + "\n", + "_PAYLOAD_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|6A|Corruption| **Target Host** HOST:FOLDERS:FOLDER:FILES:FILE:num_access HOST:num_file_creations HOST:num_file_deletions HOST:FOLDERS:FOLDER:FILES:FILE:health_status | The attacker configures the ransomware script to target the IP address of the discovered database, then it performs its intended malicious activities, allowing it to corrupt the Database whilst leaving the database service operable|\n", + "|6B|Exfiltration| **Starting Host** HOST:FOLDER:FILES:FILE HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* HOST:APPLICATIONS:APPLICATION:* | The attacker remotely logins into the target host and exfiltrates the database.db file onto the starting host which then relays this file back to the C2 Server.|\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Notebook Setup** | **Network Configuration:**\n", + "\n", + "This notebook uses the same network setup as UC7. Please refer to the main [UC7-E2E-Demo notebook for further reference](./UC7-E2E-Demo.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "

TAP001 Mobile Malware Kill Chain Demonstration - Ransomware Variant

\n", + "\n", + "_(Click to enlarge)_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['io_settings']['save_agent_actions'] = True # Saving attacker logs\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.game.simulation.network.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Setup** | Instantiating Relevant Simulation Objects\n", + "Simulation objects can be instantiated and called independently of agents via the environment.game.simulation (PrimAITE API).\n", + "\n", + "[Please refer to the main UC7 notebook for further details regarding agent implementations and the general UC7 scenario.](./example layout-E2E-Demo.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset() # resetting the environment\n", + "# The TAP001 Agent\n", + "tap001 = env.game.agents.get(\"attacker\")\n", + "# A potential starting client\n", + "starting_host = env.game.simulation.network.get_node_by_hostname('ST-PROJ-A-PRV-PC-1')\n", + "\n", + "# The database server which acts as the initial target of the ransomware kill-chain\n", + "database_server = env.game.simulation.network.get_node_by_hostname('ST-DATA-PRV-SRV-DB')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Prior To Attack** | Initial States:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first section of this notebook displays the relevant default Observation Space (OBS)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Initial State** | Starting Client:\n", + "The starting point of the ransomware infection. This is where the kill chain attack originates from." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host.show()\n", + "starting_host.file_system.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Initial State** | Database Server:\n", + "\n", + "TAP001 opts to attack the UC7 database server (`ST-DATA-PRV-SRV-DB`):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "database_server.software_manager.show()\n", + "database_server.file_system.show()\n", + "database_server.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Kill Chain** | Kill Chain Stage Demonstration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | NOT STARTED\n", + "\n", + "This stage indicates that the TAP001 hasn't begun it's kill chain. \n", + "TAP001 will begin the Mobile Malware Kill chain on the next execution step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset();\n", + "tap001 = env.game.agents.get(\"attacker\")\n", + "# The TAP001's Starting Client:\n", + "starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node)\n", + "# The TAP001's Database Server:\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST-DATA-PRV-SRV-DB')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.NOT_STARTED):\n", + " default_obs, _, _, _, _ = env.step(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | DOWNLOAD\n", + "\n", + "|Index|Action Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|1|DOWNLOAD|HOST:FOLDER:FILE:*|The `some_tech` employee encounters a malicious payload through web browsing on his mobile phone, often disguised as legitimate content.|\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + "

\n", + "\n", + "In this stage, TAP001 uses the **NODE_FOLDER_CREATE** and **NODE_FILE_CREATE** to create a file called ```\"malware_dropper.ps1\"``` within a ```\"Downloads\"``` folder.
\n", + "These actions are intended to simulate the malicious payload creating a ```ps1``` (A windows powershell script) malware dropper on the `SOME_TECH` employee's phone. \n", + "\n", + "Currently, PrimAITE cannot simulate hosts joining the simulation mid-episode thus we must treat `ST-PROJ-A-PRV-PC-1`'s as also including the employee's phone.
\n", + "From a narrative perspective, this could be explained as the employee plugging his phone into the `ST-PROJ-A-PRV-PC-1`.\n", + "\n", + "Additionally, it's worth noting that in the real world, malware droppers (small scripts or executables which download/install the malware after initially entering a host) use a variety of obfuscation methods to avoid detection.
For example, some malware droppers are concealed within legitimate files such as word document macros in order to trick a user into running the dropper.\n", + "\n", + "Currently, PrimAITE's simulation does not operate a high enough fidelity to faithfully represent these techniques and their impacts to warrant their inclusion. Thus the file has been named as ```\"malware_dropper\"``` to prevent any potential confusion." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.DOWNLOAD):\n", + " download_obs_impact, _, _, _, _ = env.step(0)\n", + "starting_host.file_system.show(full=True)\n", + "starting_host.file_system.show_num_files()\n", + "display_obs_diffs(default_obs, download_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | INSTALL\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|2|Install | HOST:FOLDER:FILES:num_access |The malware is activated, either by the employee opening the file or automatically upon download, initiating its malicious functions.|\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "In this stage, TAP001 uses the **NODE_FILE_ACCESS** to increase the number of accesses of the ```\"malware_dropper.ps1\"```.
\n", + "\n", + "These actions represent the employee executing malware dropper created in the previous stage. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.INSTALL):\n", + " install_obs_impact, _, _, _, _ = env.step(0)\n", + "\n", + "malware_file = starting_host.file_system.get_file(\"downloads\",\"malware_dropper.ps1\")\n", + "malware_file.show()\n", + "\n", + "display_obs_diffs(download_obs_impact, install_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | ACTIVATE\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|3|Activate | HOST:APPLICATION:operating_status |The malware installs itself on the terminal, often seeking to gain persistence by embedding itself into system processes or startup routines.|\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "In this stage, TAP001 uses the **NODE_APPLICATION_INSTALL** to install the ransomware application onto the starting host.
\n", + "\n", + "These actions represent the malware dropper successfully installing ransomware on the host machine. Similarly to the malware dropper, the ransomware currently implemented is intended to be a generic and OS agnostic ransomware which is not intended to represent any specific real world implementation. \n", + "\n", + "Please see the [Ransomware Notebook](./Ransomware-Kill-Chain-E2E.ipynb) for further information about the current implementation of the ransomware application.\n", + "\n", + "Future versions of PrimAITE intend to expand the capability of the ransomware application to more faithfully represent a real-world example; for example, a Trickbot variation such as Ryuk or Conti." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.ACTIVATE):\n", + " activate_obs_impact, _, _, _, _ = env.step(0)\n", + "starting_host.software_manager.show()\n", + "display_obs_diffs(install_obs_impact, activate_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | PROPAGATE\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|4|Propagate|HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:*|The malware attempts to spread to other systems or networks, looking for vulnerable services.|\n", + "\n", + "In this stage, TAP001 uses **NODE_NMAP_PORT_SCAN**, **NODE_NMAP_PING_SCAN** and **NODE_NMAP_NETWORK_SERVICE_RECON** to scan the simulation in order to search for a valid database target.\n", + "\n", + "Unlike previous stages, the behaviour of this stage is dependant on the simulation and thus will perform differently dependant on the location of the target as well as the topology of the network. Specifically, the ```PROPAGATE``` stage uses three network enumeration actions and their action responses to populate its knowledge of the network.
\n", + "These actions represent the now infected `ST-PROJ-A-PRV-PC-1` searching the UC7 network for valid targets ransomware. \n", + "\n", + "For more information around how agent requests and responses work then the [request-response notebook can provide some useful insights](./Requests-and-Responses.ipynb).\n", + "\n", + "It's worth noting that the implementation of the ```PROPAGATE``` stage does not class routers and switches as valid targets and thus will not scan them.
\n", + "\n", + "_Currently, red agents have no way of probing the routing information of the simulation so must be provided the network addresses of the network.
See the mobile malware configuration option section of this notebook for further information_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | Scan walkthrough\n", + "\n", + "The next juypter cells of this notebook will go through each individual CAOS action that the TAP001 leverages to reach the target host as well as the OBS each action impacts.\n", + "\n", + "This section uses the following ```PROPAGATE``` relevant TAP001 settings:\n", + "```yaml\n", + " kill chain:\n", + " PROPAGATE:\n", + " probability: 1\n", + " scan_attempts: 20\n", + " repeat_scan: false\n", + " network_addresses:\n", + " - 192.168.230.0/29 # ST Project A\n", + " - 192.168.10.0/26 # Remote Site\n", + " - 192.168.20.0/30 # Remote DMZ\n", + " # - 192.168.240.0/29 # ST Project B\n", + " # - 192.168.250.0/29 # ST Project C\n", + " - 192.168.220.0/29 # ST Data (Contains Target)\n", + "```\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "\n", + "def propagate_obs_and_show(tap001: TAP001, previous_obs, propagate_obs_impact, timestep):\n", + " tap001._show_scan()\n", + " display_obs_diffs(previous_obs, propagate_obs_impact, timestep)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | ST-PROJ-A-PRV-PC-1\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ping Scan\n", + "for _ in range(5):\n", + " PROJ_A_SITE_ping, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=activate_obs_impact, propagate_obs_impact=PROJ_A_SITE_ping, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Recon Scan\n", + "for _ in range(5):\n", + " PROJ_A_SITE_recon, *_ = env.step(0);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, PROJ_A_SITE_ping, PROJ_A_SITE_recon, env.game.step_counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | REMOTE SITE\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ping Scan\n", + "for _ in range(5):\n", + " REMOTE_SITE_ping, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=activate_obs_impact, propagate_obs_impact=REMOTE_SITE_ping, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Recon Scan\n", + "for _ in range(5):\n", + " REMOTE_SITE_recon, *_ = env.step(0);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, REMOTE_SITE_ping, REMOTE_SITE_recon, env.game.step_counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | REMOTE DMZ\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ping Scan\n", + "for _ in range(5):\n", + " REMOTE_DMZ_ping, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=REMOTE_SITE_recon, propagate_obs_impact=REMOTE_DMZ_ping, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Recon Scan\n", + "for _ in range(5):\n", + " REMOTE_DMZ_recon, *_ = env.step(0);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=REMOTE_DMZ_ping, propagate_obs_impact=REMOTE_DMZ_recon, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | SOME_TECH DATA \n", + "\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ping Scan\n", + "for _ in range(5):\n", + " ST_DATA_ping, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=REMOTE_DMZ_recon, propagate_obs_impact=ST_DATA_ping, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Recon Scan\n", + "for _ in range(5):\n", + " ST_DATA_recon, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=ST_DATA_ping, propagate_obs_impact=ST_DATA_recon, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for _ in range(5):\n", + " propagate_obs_impact, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001._show_scan()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | COMMAND_AND_CONTROL\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|5|Command and Control| **Starting Host** HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* HOST:APPLICATIONS:APPLICATION:* |The malware establishes a connection to an external command and control (C&C) server, receiving instructions and possibly exfiltrating data.|\n", + "\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "\n", + "For further details please refer to the ``Command-and-Control-E2E-Demonstration`` notebook.\n", + "\n", + "_Note: The referenced notebook above uses the UC2 scenario for demonstration purposes, however all the OBS impacts and C2 suite functionality is equally applicable to UC7._\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.COMMAND_AND_CONTROL):\n", + " c_and_c_obs_impact, _, _, _, _ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "display_obs_diffs(propagate_obs_impact, c_and_c_obs_impact,env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | PAYLOAD - Corruption & Exfiltration\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|6A|Corruption| **Target Host** HOST:FOLDERS:FOLDER:FILES:FILE:num_access HOST:num_file_creations HOST:num_file_deletions HOST:FOLDERS:FOLDER:FILES:FILE:health_status | The attacker configures the ransomware script to target the IP address of the discovered database, then it performs it's intended malicious activities, allowing it to corrupt the Database whilst leaving the database service operable|\n", + "|6B|Exfiltration| **Starting Host** HOST:FOLDER:FILES:FILE HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* HOST:APPLICATIONS:APPLICATION:* | The attacker remotely logins into the target host and exfiltrates the database.db file onto the starting host which then relays this file back to the C2 Server.|\n", + "\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.PAYLOAD):\n", + " payload_obs_impact, _, _, _, _ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "target_host.file_system.show(full=True)\n", + "display_obs_diffs(c_and_c_obs_impact, payload_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can use the blue agent's NODE_FILE_SCAN action to scan the database file:\n", + "\n", + "```yaml\n", + "# ST-DATA-PRV-SRV-DB | node-file-scan | Scans the database.db file (health status)\n", + "49:\n", + " action: node-file-scan\n", + " options:\n", + " node_name: ST-DATA-PRV-SRV-DB\n", + " folder_name: database\n", + " file_name: database.db\n", + "```\n", + "\n", + "*You should notice a file `health_status` change to a value of 3*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(0)\n", + "post_scan_payload_impact, _, _, _, _ = env.step(49) # \n", + "display_obs_diffs(payload_obs_impact, post_scan_payload_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "target_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also see that the database.db file was successfully exfiltrated." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "c2_server = env.game.simulation.network.get_node_by_hostname(tap001.c2_settings['c2_server'])\n", + "starting_host.file_system.show(full=True)\n", + "c2_server.file_system.show(full=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Post Attack Impacts**\n", + "\n", + "Please refer to the [Ransomware E2E Notebook](./Ransomware-Kill-Chain-E2E.ipynb) for an in-depth look on the knock-on affects of the ransomware application." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Threat Actor Profile Settings" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All TAP's inherit the same general configurability shown in the following table:\n", + "\n", + "
TAP Agent Config Options \n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|start_step|The initial kill chain starting step.|Int|_Required_|\n", + "|frequency|The frequency of each kill chains starting steps.|Int|_Required_|\n", + "|variance| The timestep variance between frequency|Int|_Required_|\n", + "|repeat_kill_chain|Indicates whether the attack is repeated throughout the episode.|Bool|_Required_|\n", + "|repeat_kill_chain_stages|Indicates if the kill_chain stage should reset upon failure or retry.|Bool|_Required_|\n", + "|default_target_ip | The IP address of the target host |Str|_Required_|\n", + "|default_target_node|The Target Host|Str|_Required_|\n", + "|target_nodes|A list of Potential Target Hosts (database services) - Selected on per episode basis.|List|_Optional_|\n", + "|default_starting_node|The Starting Host|Str|_Required_|\n", + "|starting_nodes|A list of Potential Targets|List|_Optional_|\n", + "|kill_chain|TAP001 Specific Config (_See the next notebook section_)|Dict|_Required_|\n", + "|description|Free Text|Str|_Optional_|\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```yaml\n", + "\n", + " - ref: attacker\n", + " team: RED\n", + " type: TAP001\n", + " agent_settings:\n", + " start_step: 1\n", + " frequency: 3\n", + " variance: 0\n", + " repeat_kill_chain: false\n", + " repeat_kill_chain_stages: true\n", + " default_target_ip: *ST_SRV_DB_IP\n", + " default_starting_node: \"ST-PROJ-A-PRV-PC-1\"\n", + " # starting_nodes: [\"ST-PROJ-A-PRV-PC-1\", \"ST-PROJ-B-PRV-PC-2\", \"ST-PROJ-C-PRV-PC-3\"]\n", + " kill_chain:\n", + " ... # Next notebook section will cover this configuration option\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook assumes the reader is already familiar with the below `agent_settings` and won't provide any code snippet examples. \n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|start_step|The initial kill chain starting step.|Int|_Required_|\n", + "|frequency|The frequency of each kill chains starting steps.|Int|_Required_|\n", + "|variance| The timestep variance between frequency|Int|_Required_|\n", + "|repeat_kill_chain|Indicates whether the attack is repeated throughout the episode.|Bool|_Required_|\n", + "|repeat_kill_chain_stages|Indicates if the kill_chain stage should reset upon failure or retry.|Bool|_Required_|\n", + "|default_target_node|The Target Host|Str|_Required_|\n", + "|starting_nodes|A list of Potential Targets|List|_Optional_|\n", + "|kill_chain|_See the next notebook section_|Dict|_Required_||\n", + "\n", + "\n", + "If you're unfamiliar with the above then refer to the equivalent `agent_settings` section within the [TAP003 Kill Chain E2E Demonstration notebook](./TAP003-Kill-Chain-E2E.ipynb). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001 = env.game.agents.get(\"attacker\")\n", + "for key,value in tap001.config.agent_settings:\n", + " if key == 'kill_chain':\n", + " pass\n", + " else:\n", + " print(f\"{key} : {value}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Kill Chain Settings\n", + "\n", + "Additionally, TAP001's Mobile Malware Kill Chain comes with some extra configuration options. \n", + "\n", + "These options can be configured to customise the behaviour of certain stages. \n", + "\n", + "The YAML snippet below is the current default configuration of the mobile malware kill chain:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``` YAML\n", + " - ref: attacker\n", + " team: RED\n", + " type: TAP001\n", + " agent_settings:\n", + " kill_chain:\n", + " ACTIVATE:\n", + " probability: 1\n", + " PROPAGATE:\n", + " probability: 1\n", + " scan_attempts: 20\n", + " repeat_scan: false\n", + " network_addresses:\n", + " - 192.168.230.0/29 # ST Project A\n", + " - 192.168.10.0/26 # Remote Site\n", + " - 192.168.20.0/30 # Remote DMZ\n", + " # - 192.168.240.0/29 # ST Project B\n", + " # - 192.168.250.0/29 # ST Project C\n", + " - 192.168.220.0/29 # ST Data (Contains Target)\n", + " COMMAND_AND_CONTROL:\n", + " probability: 1\n", + " keep_alive_frequency: 5\n", + " masquerade_port: HTTP\n", + " masquerade_protocol: TCP\n", + " c2_server_name: ISP-PUB-SRV-DNS\n", + " c2_server_ip: *PUBLIC_DNS_IP\n", + " PAYLOAD:\n", + " probability: 1\n", + " exfiltrate: true\n", + " corrupt: true\n", + " exfiltration_folder_name:\n", + " target_username: admin\n", + " target_password: admin\n", + " continue_on_failed_exfil: True\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Mobile Malware Kill Chain | General Settings" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "TAP001's ``ACTIVATE``, ``PROPAGATE``, ``COMMAND_AND_CONTROL`` and ``PAYLOAD`` stages's probability of success can be configured.\n", + "\n", + "Similarly to other TAPs, the argument given to probability is the chance of action performing that stage of kill chain successfully. \n", + "The argument given is expected to be between **0** - **1**. \n", + "\n", + "_(With '1' equalling 100% chance of 'success')_\n", + "\n", + "It's important to note that the probabilities of success are calculated within the game layer meaning that if an TAP fails because of probability then the TAP will perform a ``DONOTHING`` action for that step. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```yaml\n", + " kill_chain:\n", + " ACTIVATE:\n", + " probability: 1\n", + " PROPAGATE:\n", + " probability: 1\n", + " COMMAND_AND_CONTROL:\n", + " probability: 1\n", + " PAYLOAD:\n", + " probability: 1\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Mobile Malware Kill Chain | Propagate Stage\n", + "\n", + "TAP001's propagate step leverages the NMAP application to scan the network. This kill chain stage is a considerably more complex than the other stages and thus has more configuration options.\n", + "\n", + "
Propagate Configuration Settings \n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Action Probability - This is only calculated on the initial scan.|int|_Required_|\n", + "|network_addresses|The network-addresses that are scanned during the propagate step. Scanned in the order they are defined.|list[str]|_Required_|\n", + "|scan_attempts|The amount of permitted scan attempts.|int|_Required_|\n", + "|repeat_scan|Should the scan repeat if the target is not found within the given network address|bool|_optional_|\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Probability**\n", + "\n", + "***\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Action Probability |Int|_Required_|\n", + "\n", + "Similarly, to every other stage, probability is the chance of success of TAP001 has to successfully perform the Propagate Stage.\n", + "\n", + "However, it's important to note that the **probability of success is only calculated once**. \n", + "\n", + "After the first scan is performed, the TAP agent will perform the rest of the stage without trialing probability." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Network Addresses**\n", + "\n", + "***\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|network_addresses|The network-addresses that are scanned during the propagate step. Scanned in the order they are defined.|list[str]|_Required_|\n", + "\n", + "At present, TAPs cannot probe the simulation for routing information.\n", + "\n", + "Therefore, to scan multiple networks, the ``PROPAGATE`` stage must be provided a list of each network address.\n", + "\n", + "These network's are scanned in sequential order.\n", + "\n", + "For example; this notebook is configured with the following ``PROPAGATE`` network_address setting.\n", + "\n", + "| Network Address | Use Case 7 Subnet Name |\n", + "|-----------------|-------------------------|\n", + "| 192.168.10.0/26 | REMOTE-SITE |\n", + "| 192.168.20.0/30 | REMOTE-DMZ |\n", + "| 192.168.220.0/29 | SOME_TECH_DATA |\n", + "\n", + "\n", + "Which when represented in the yaml config is as follows:\n", + "\n", + "```yaml\n", + " kill_chain:\n", + " PROPAGATE:\n", + " probability: 1\n", + " scan_attempts: 20\n", + " repeat_scan: false\n", + " network_addresses:\n", + " - 192.168.230.0/29 # ST Project A\n", + " - 192.168.10.0/26 # Remote Site\n", + " - 192.168.20.0/30 # Remote DMZ\n", + " # - 192.168.240.0/29 # ST Project B\n", + " # - 192.168.250.0/29 # ST Project C\n", + " - 192.168.220.0/29 # ST Data (Contains Target)\n", + "```\n", + "Which is loaded into the TAP001 agent as the following list:\n", + "```Python\n", + "[\"192.168.230.0/29\", \"192.168.10.0/26\", \"192.168.20.0/30\", \"192.168.220.0/29\"]\n", + "```\n", + "\n", + "As PrimAITE expands and TAP agents are provided more ways of probing the simulation, then the ```PROPAGATE``` stage will be able to perform independently thus no longer requiring the network_address configuration option. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Scan Attempts**\n", + "\n", + "***\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|scan_attempts|The amount of permitted scan attempts.|int|_Required_|\n", + "\n", + "Simply, the ``scan_attempts`` configuration option indicates how many SCAN actions ``PROPAGATE`` stage is permitted to perform before the kill chain is considered to have failed. \n", + "\n", + "The ``scan_attempts`` option should mainly be kept higher than the length of the list provided in the previous ``network_address`` option.\n", + "\n", + "Currently, this setting is mainly used in conjunction with next setting: ```repeat_scan```.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Repeat Scan**\n", + "\n", + "***\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|repeat_scan|Should the scan repeat if the target is not found within the given network address|bool|_optional_|\n", + "\n", + "This boolean flag controls whether the ```PROPAGATE``` stage should repeat if the target is not found within the given ```network_addresses```.\n", + "\n", + "This setting used in conjunction with the ```permitted_scan``` option allows the Red Agent to continue scanning even if the target is not found. \n", + "\n", + "The network addresses will be selected at random after the first sequential scan(s) of the given ```network_addresses``` for further domain randomisation. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Removing the target network address. (Thus the target will never be found)\n", + "network_addresses = [\"192.168.10.0/26\", \"192.168.20.0/30\"]\n", + "\n", + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = True\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PROPAGATE']['network_addresses'] = network_addresses\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PROPAGATE']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PROPAGATE']['scan_attempts'] = 30\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PROPAGATE']['repeat_scan'] = True\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range (256):\n", + " env.step(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "[1] _PrimAITE does not actually enforce agent type (Red/Green/Blue) specific actions_\n", + "\n", + "_However, some actions such as ``APPLICATION_EXECUTE`` and ``NETWORK_RECON_SCAN`` require an understanding of the simulation that is beyond the blue agent's current observation and thus are not suitable for use by reinforcement algorithms._\n", + "\n", + "_These actions are usually only leveraged by Green or Red agents; hence why they are commonly referenced as such._\n", + "\n", + "***" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Mobile Malware Kill Chain | Command and Control Stage\n", + "\n", + "TAP001's Command and Control stage leverages the C2 beacon which has it's own set of configuration options. In the case of TAP001 some of these settings are already pre-defined based on other settings such as ``target_node``. The table below details the currently available options.\n", + "\n", + "
Command and Control Configuration Settings \n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|probability | Action Probability - This is only calculated once at this stage. |Int | _Required_ |\n", + "|c2_server | What host should the C2 Beacon attempt to connect to as the chosen C2 Server |Str | _Required_ |\n", + "|keep_alive_frequency | How often should the C2 Beacon confirm its connection in timesteps. Defaults to 5 |Int | _Optional_ |\n", + "|masquerade_port | What port should the C2 traffic use? Defaults to TCP. |Str | _Optional_ |\n", + "|masquerade_protocol | What protocol should the C2 traffic masquerade as? Defaults to HTTP. |Str | _Optional_ |\n", + "
\n", + "\n", + "For further information around the configuration of the C2 beacon please refer to the ``Command-&-Control-E2E-Demonstration`` last section on configurability." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " # Adding the C2 Server to a different node (REM-PUB-PC-1)\n", + " cfg['simulation']['network']['nodes'][10].update(\n", + " {\"applications\": [\n", + " {\"type\": \"database-client\", \"options\":{\"db_server_ip\":\"192.168.220.3\"}},\n", + " {\"type\": \"web-browser\", \"options\":{\"target_url\": \"http://some_tech.com\"}},\n", + " {\"type\": \"c2-server\", \"options\":{\"listen_on_ports\":[21]}}]\n", + " })\n", + "\n", + " # Configuring the C2 stage to use the REM-PUB-PC-1 as it's C2 Server and to use a different masquerade port.\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"c2_server_name\"] = \"REM-PUB-PC-1\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"c2_server_ip\"] = \"192.168.20.2\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"keep_alive_frequency\"] = 3\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"masquerade_port\"] = \"FTP\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"masquerade_protocol\"] = \"TCP\"\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "# TAP001 runs for exactly 110 timesteps using default TAP settings.\n", + "for _ in range(110):\n", + " env.step(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cells below use .show() methods to show that the configuration options have successfully altered the C2's suite configuration. For example the C2 beacon's remote connection is now ``REM-PUB-PC-1``'s ip address which is ``192.168.20.2``." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST-PROJ-A-PRV-PC-1')\n", + "c2_beacon = starting_host.software_manager.software[\"c2-beacon\"]\n", + "c2_beacon.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "c2_server_host = env.game.simulation.network.get_node_by_hostname('REM-PUB-PC-1')\n", + "c2_server = c2_server_host.software_manager.software[\"c2-server\"]\n", + "c2_server.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Mobile Malware Kill Chain | Payload Stage\n", + "\n", + "
Payload Settings \n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|probability | Action Probability - This is only calculated once at this stage. |Int | _Required_ |\n", + "|corrupt | Should TAP001 launch the ransomware script against the target database? |Boolean | _Required_ |\n", + "|exfiltrate | Should TAP001 exfiltrate the target database.db file? |Boolean | _Required_ |\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Probability**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|probability | Action Probability - This is only calculated once at this stage. |Int | _Required_ |\n", + "\n", + "Similarly, to every other stage, probability is the chance of success of TAP001 has to successfully perform the Payload Stage.\n", + "\n", + "However, it's important to note that the **probability of success is only calculated once**. \n", + "\n", + "After the agent is successful once then the TAP agent will perform the rest of the stage without trialing any further." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Corrupt**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|corrupt | Should TAP001 launch the ransomware script against the target database? |Boolean | _Required_ |\n", + "\n", + "This option is a boolean value which indicates if TAP001 should launch the ransomware script against the target database. \n", + "\n", + "By default this is enabled but if users wish to disable the ransomware attack for training purposes then this value can be set to ``False`` which will prevent the installed ``RansomwareScript`` from launching at the final step.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exfiltrate**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|exfiltrate | Should TAP001 exfiltrate the target database.db file? |Boolean | _Required_ |\n", + "\n", + "\n", + "Similar to ``corrupt``, this option is a boolean value which indicates if TAP001 should attempt to exfiltrate the database.db file.\n", + "\n", + "By default this is enabled but if users wish to disable the exfiltration for training purposes then this value can be set to ``False`` which will prevent the TAP001 agent from attempting to exfiltrate the database.db file." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_If both ``exfiltrate`` and ``corrupt`` options are enabled then the TAP001 agent will exfiltrate the database.db and then launch the ``RansomwareScript`` against the target._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Yaml Example:\n", + "\n", + "```yaml\n", + " kill_chain:\n", + " PAYLOAD:\n", + " probability: 1\n", + " exfiltrate: true\n", + " corrupt: true\n", + " exfiltration_folder_name:\n", + " target_username: admin\n", + " target_password: admin\n", + " continue_on_failed_exfil: True\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following code cells demonstrate how ``corrupt`` and ``exfiltrate`` can be enabled/disabled and their effects on the environment. After each example the file systems of the target and starting hosts are displayed. If ``corrupt`` is enabled then the target database file will be encrypted and if the ``exfiltrate`` option is enabled then the target database.db will be present within the starting host's file system." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"corrupt\"] = True\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"exfiltrate\"] = False\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST-PROJ-A-PRV-PC-1')\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST-DATA-PRV-SRV-DB')\n", + "target_host.file_system.show(full=True)\n", + "starting_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"corrupt\"] = False\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"exfiltrate\"] = True\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST-PROJ-A-PRV-PC-1')\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST-DATA-PRV-SRV-DB')\n", + "target_host.file_system.show(full=True)\n", + "starting_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"corrupt\"] = True\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"exfiltrate\"] = True\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST-PROJ-A-PRV-PC-1')\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST-DATA-PRV-SRV-DB')\n", + "target_host.file_system.show(full=True)\n", + "starting_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Payload Stage | Exfiltration Settings\n", + "\n", + "If TAP001 is configured to exfiltrate the database.db file then the following configuration options are relevant.\n", + "\n", + "
Exfiltration Settings \n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|-------------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|exfiltration_folder_name | The folder used to store the database.db file. Defaults to ``exfiltration_folder`` |Str | _Optional_ |\n", + "|target_username | The username used to login into a target node. Defaults to ``admin`` |Str | _Required_ |\n", + "|target_password | The password used to login into a target node. Defaults to ``admin`` |Str | _Required_ |\n", + "|continue_on_failed_exfil | Indicates if the attacker should encrypt the target even if the exfiltration fails |Bool | _Required_ |\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**exfiltration_folder_name**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|-------------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|exfiltration_folder_name | The folder used to store the database.db file. Defaults to ``exfiltration_folder``.|str | _Optional_ |\n", + "\n", + "Users are able to configure what folder the database.db is stored within after successful database exfiltration. For example, if this option was set to ``crown_jewels`` then the TAP001 agent would create a new folder on the starting node called ``crown_jewels`` and once the data-exfiltration is successful that folder will be populated with the ``database.db`` file from the target database. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"exfiltration_folder_name\"] = \"crown_jewels\"\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST-PROJ-A-PRV-PC-1')\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST-DATA-PRV-SRV-DB')\n", + "target_host.file_system.show(full=True)\n", + "starting_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**target_username & target_password**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|-------------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|target_username | The username used to login into a target node. Defaults to ``admin`` |Str | _required_ |\n", + "|target_password | The password used to login into a target node. Defaults to ``admin`` |Str | _required_ |\n", + "\n", + "These fields indicate what user credentials the TAP001 agent will use when attempting to exfiltrate the `database.db` file from the target." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Creating a new user for the TAP001 to use.\n", + "users = [{\n", + " \"username\": \"example_user\",\n", + " \"password\": \"example_pass\",\n", + " \"is_admin\": \"False\",\n", + "}]\n", + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['simulation']['network']['nodes'][28].update({\"users\":users}) # Adding this new user to the target\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"target_username\"] = \"example_user\" # Setting TAP001 to use the new user credentials.\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"target_password\"] = \"example_pass\"\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "target_host = env.game.simulation.network.get_node_by_hostname('ST-DATA-PRV-SRV-DB')\n", + "target_host.user_manager.show()\n", + "target_host.file_system.show()\n", + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**continue_on_failed_exfil**\n", + "\n", + "***\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|-------------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|continue_on_failed_exfil | Indicates if the attacker should encrypt the target even if the exfiltration fails |Bool | _Required_ |\n", + "\n", + "\n", + "This option will affect how the TAP001 agent responds to failing the exfiltration. If this option is set to `True` then the TAP001 agent will attempt the final attack against the database even if the `database.db` exfiltration attempt is unsuccessful. Likewise, if this option is set to `False` then the TAP001 agent will consider it's attack failed if it cannot complete the exfiltration.\n", + "\n", + "The code cells below demonstrate the differences by using the blue agent to change the user account details on the target database before the exfiltration occurs using action 50\n", + "\n", + "```yaml\n", + " # ST-DATA-PRV-SRV-DB | node-account-change-password | Changes the password of a user account\n", + " 50:\n", + " action: node-account-change-password\n", + " options:\n", + " node_name: ST-DATA-PRV-SRV-DB\n", + " username: admin # default account\n", + " current_password: admin # default password\n", + " new_password: thr33_alert_wolv3z # A more 'secure' password\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we set the `continue_on_failed_exfil` to true we can see that the despite the exfiltration failing the `database.db` still ends up corrupted by the end of the attack." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"continue_on_failed_exfil\"] = True\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "\n", + "# Changing the target database credentials:\n", + "env.step(50)\n", + "\n", + "# Finishing the episode:\n", + "for _ in range(127):\n", + " env.step(0)\n", + " \n", + "tap001.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The exfiltration was not successful\n", + "starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node)\n", + "\n", + "starting_host.file_system.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Yet the target database.db is still corrupt.\n", + "\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST-DATA-PRV-SRV-DB')\n", + "target_host.file_system.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Whereas now if we set `continue_on_failed_exfil` to false we can see that the attack failed and the database health status remains `GOOD`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"continue_on_failed_exfil\"] = False\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "\n", + "# Changing the target database credentials:\n", + "env.step(50)\n", + "\n", + "# Finishing the episode:\n", + "for _ in range(127):\n", + " env.step(0)\n", + " \n", + "tap001.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The exfiltration was not successful\n", + "starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node)\n", + "\n", + "starting_host.file_system.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# And the target database.db remains healthy.\n", + "\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST-DATA-PRV-SRV-DB')\n", + "target_host.file_system.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-TAP003-Kill-Chain-E2E.ipynb b/src/primaite/notebooks/UC7-TAP003-Kill-Chain-E2E.ipynb new file mode 100644 index 00000000..365b01ed --- /dev/null +++ b/src/primaite/notebooks/UC7-TAP003-Kill-Chain-E2E.ipynb @@ -0,0 +1,1687 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Backdoor & Vulnerability Creation Kill Chain\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n", + "\n", + "**Threat Actor Profile (TAP):** 003
\n", + "**Kill Chain**: Backdoor & Vulnerability Creation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook demonstrates a new UC7 Kill Chain which aims to represent a different style of attack in comparison to the mobile malware kill chain (TAP001).\n", + "Kill chains aim to represent a potential real world cyber attack. In this scenario an malicious `SOME_TECH` admin (TAP003) leverages his legitimate credentials and permissions to create purposeful backdoors, establish footholds and use other legitimate features in malicious ways.\n", + "
\n", + "\n", + "In this version - this scenario is limited in scope. TAP003 opts to alter user accounts and implement malicious ACL rule by using the ``terminal`` service to SSH into target routers. These ACLs block green traffic which trigger a negative reward.\n", + "
\n", + "\n", + "This kill chain intends to introduce a new UC7 attack which is both realistic but also dissimilar to other kill chains." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "# Importing the necessary primAITE libraries\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite.simulator.system.applications.database_client import DatabaseClient\n", + "from primaite.simulator.system.services.dns.dns_client import DNSClient\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "from primaite.simulator.system.applications.web_browser import WebBrowser\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.config.load import load, _EXAMPLE_CFG\n", + "from pprint import pprint\n", + "from deepdiff.diff import DeepDiff\n", + "from prettytable import PrettyTable\n", + "import yaml" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Utility functions.\n", + "\n", + "def print_agent_actions_except_do_nothing(agent_name):\n", + " \"\"\"\n", + " Print an agent action provided it's not the DONOTHING action.\n", + "\n", + " :param agent_name: Name of agent (str).\n", + " \"\"\"\n", + " table = PrettyTable()\n", + " table.field_names = [\"Step\", \"Action\", \"Node\", \"Application\", \"Response\"]\n", + " print(f\"Episode: {env.episode_counter}, Actions for '{agent_name}':\")\n", + " for item in env.game.agents[agent_name].history:\n", + " if item.action != \"DONOTHING\":\n", + " node, application = 'unknown', 'unknown'\n", + " if (node_id := item.parameters.get('node_id')) is not None:\n", + " node = env.game.agents[agent_name].action_manager.node_names[node_id]\n", + " if (application_id := item.parameters.get('application_id')) is not None:\n", + " application = env.game.agents[agent_name].action_manager.application_names[node_id][application_id]\n", + " if (application_name := item.parameters.get('application_name')) is not None:\n", + " application = application_name\n", + " table.add_row([item.timestep, item.action, node, application, item.response.status])\n", + " print(table)\n", + " print(\"(Any DONOTHING actions are omitted)\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def display_obs_diffs(old, new, step_counter):\n", + " \"\"\"\n", + " Use DeepDiff to extract and display differences in old and new instances of\n", + " the observation space.\n", + "\n", + " :param old: observation space instance.\n", + " :param new: observation space instance.\n", + " :param step_counter: current step counter.\n", + " \"\"\"\n", + " print(\"\\nObservation space differences\")\n", + " print(\"-----------------------------\")\n", + " diff = DeepDiff(old, new)\n", + " print(f\"Step {step_counter}\")\n", + " for d,v in diff.get('values_changed', {}).items():\n", + " print(f\"{d}: {v['old_value']} -> {v['new_value']}\")\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Sections:**\n", + "1. Notebook Intro\n", + "2. Notebook Setup\n", + "3. Prior To Attack\n", + "4. Kill Chain Stage Demonstration\n", + "5. Attack Configurations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Intro** | **Backdoor & Vulnerability Creation Kill Chain Intro** \n", + "\n", + "TAP003's kill chain is comprised of a variety of blue actions which are leveraged in unusual ways. This includes introducing malicious ACL's which block green traffic and installing and execute green applications in order to simulate unusual green pattern of life. The rest of this notebook will go through each step in more detail whilst demonstrating the impacts that each step has on both observation and simulation behaviour.\n", + "\n", + "_Reconnaissance - DONOTHING CAOS Action_\n", + "\n", + "|Index | Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|-------------|-----------|\n", + "|1|Reconnaissance|*No Direct Impact*|TAP003 is passively investigating sensitive systems, data and access control mechanisms.|\n", + "\n", + "_Planning - DONOTHING CAOS Action_\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|2|Planning| **No current impact**|TAP003 is devising a plan to exploit their elevated privileges.|\n", + "\n", + " _Access - DONOTHING CAOS Action__\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|3|Access|**No current impact** |TAP003 uses their legitimate credentials to access the access control settings.|\n", + "\n", + " _Manipulation - HOST:SESSIONS_SEND_REMOTE_COMMAND -> HOST:ACCOUNTS:CHANGE:PASSWORD CAOS ACTION_\n", + " \n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|4|Manipulation| **Target Host(s)** HOST::SESSIONS:REMOTE |TAP003 exploits their insider knowledge/privilege to implement changes for sabotage.|\n", + "\n", + " _Exploit - FIREWALL:ACL:add_rule CAOS ACTION_\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|5|Exploit| **Target Host(s)** FIREWALL:ACL:INTERNAL/EXTERNAL:*|TAP003 exploits their insider knowledge/privilege to implement changes for sabotage.|\n", + "\n", + "_Only the initial five steps are represented in the this version of this kill-chain._
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Notebook Setup** | **Network Configuration:**\n", + "\n", + "This notebook uses the same network setup as UC7. \n", + "\n", + "Please refer to the main [UC7-E2E-Demo notebook for further reference](./UC7-E2E-Demo.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "_(Click to enlarge)_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.game.simulation.network.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Setup** | Instantiating Relevant Simulation Objects\n", + "The cell below sets up the relevant simulation and agent objects for the kill chain demonstration.\n", + "\n", + "\n", + "The following cell resets the environment, instantiates TAP003 and it's selected starting/target hosts." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "env.reset() # resetting the environment\n", + "# The TAP003 Agent\n", + "tap003 = env.game.agents['attacker']\n", + "tap003.logger.logger.setLevel(\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Prior To Attack** | Initial States:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first section of this notebook displays the initial simulation state of TAP003's starting and target nodes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Initial State** | Starting Host:\n", + "TAP003's initial starting point.\n", + "\n", + "TAP003's starting host does not cause any MNE (Malicious Network Events) in it's current implementation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "# TAP003's Starting Client:\n", + "starting_host = env.game.simulation.network.get_node_by_hostname(tap003.starting_node)\n", + "print(f\"Starting host:\")\n", + "starting_host.show()\n", + "starting_host.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Initial State** | Target Hosts\n", + "\n", + "TAP003's selected target hosts at current the only valid targets for TAP003 are Router/Firewall type nodes\n", + "\n", + "Code snippet below shows the default OBS of the target routers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "# TAP003's target routers\n", + "\n", + "st_intra_prv_rt_cr: Router = env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_dr_1: Router = env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-DR-1\")\n", + "rem_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\")\n", + "\n", + "st_intra_prv_rt_cr.acl.show()\n", + "st_intra_prv_rt_dr_1.acl.show()\n", + "rem_pub_rt_dr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Kill Chain** | Kill Chain Stage Demonstration \n", + "\n", + "For the initial kill chain demonstration, TAP003 is configured with the following yaml snippet:\n", + "\n", + "```yaml\n", + " - ref: attacker\n", + " team: RED\n", + " type: tap-003\n", + " observation_space: {}\n", + " action_space: {}\n", + " agent_settings:\n", + " start_step: 1\n", + " frequency: 3\n", + " variance: 0\n", + " repeat_kill_chain: false\n", + " repeat_kill_chain_stages: true\n", + " default_starting_node: \"ST-PROJ-A-PRV-PC-1\"\n", + " starting_nodes:\n", + " # starting_nodes: [\"ST-PROJ-A-PRV-PC-1\", \"ST-PROJ-B-PRV-PC-2\", \"ST-PROJ-C-PRV-PC-3\"]\n", + " kill_chain:\n", + " PLANNING:\n", + " probability: 1\n", + " starting_network_knowledge:\n", + " credentials:\n", + " ST-PROJ-A-PRV-PC-1:\n", + " username: admin\n", + " password: admin\n", + " ST-PROJ-B-PRV-PC-2:\n", + " username: admin\n", + " password: admin\n", + " ST-PROJ-C-PRV-PC-3:\n", + " username: admin\n", + " password: admin\n", + " ST-INTRA-PRV-RT-DR-1:\n", + " ip_address: 192.168.230.1\n", + " username: admin\n", + " password: admin\n", + " ST-INTRA-PRV-RT-CR:\n", + " ip_address: 192.168.160.1\n", + " username: admin\n", + " password: admin\n", + " REM-PUB-RT-DR:\n", + " ip_address: 192.168.10.2\n", + " username: admin\n", + " password: admin\n", + " ACCESS:\n", + " probability: 1\n", + " MANIPULATION:\n", + " probability: 1\n", + " account_changes:\n", + " - host: ST-INTRA-PRV-RT-DR-1\n", + " ip_address: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1\n", + " action: change_password\n", + " username: admin\n", + " new_password: \"red_pass\"\n", + " - host: ST-INTRA-PRV-RT-CR\n", + " ip_address: 192.168.160.1 # ST-INTRA-PRV-RT-CR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " - host: REM-PUB-RT-DR\n", + " ip_address: 192.168.10.2 # REM-PUB-RT-DR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " EXPLOIT:\n", + " probability: 1\n", + " malicious_acls:\n", + " - target_router: ST-INTRA-PRV-RT-DR-1\n", + " position: 1\n", + " permission: DENY\n", + " src_ip: ALL\n", + " src_wildcard: 0.0.255.255\n", + " dst_ip: ALL\n", + " dst_wildcard: 0.0.255.255\n", + " src_port: POSTGRES_SERVER\n", + " dst_port: POSTGRES_SERVER\n", + " protocol_name_name: TCP\n", + " - target_router: ST-INTRA-PRV-RT-CR\n", + " position: 1\n", + " permission: DENY\n", + " src_ip: ALL\n", + " src_wildcard: 0.0.255.255\n", + " dst_ip: ALL\n", + " dst_wildcard: 0.0.255.255\n", + " src_port: HTTP\n", + " dst_port: HTTP\n", + " protocol_name_name: TCP\n", + " - target_router: REM-PUB-RT-DR\n", + " position: 1\n", + " permission: DENY\n", + " src_ip: ALL\n", + " src_wildcard: 0.0.255.255\n", + " dst_ip: ALL\n", + " dst_wildcard: 0.0.255.255\n", + " src_port: DNS\n", + " dst_port: DNS\n", + " protocol_name_name: TCP\n", + "\n", + "```\n", + "_Further information & guidance around configuring TAP003 can be found within the last section of this notebook._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | NOT STARTED\n", + "This is the default state of the TAP003 Agent. This stage indicates that the TAP003 agent has not begun it's kill chain.\n", + "If TAP003 is in this stage then on the next execution step the kill chain will begin." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003 = env.game.agents['attacker']\n", + "\n", + "\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.NOT_STARTED):\n", + " default_obs, _, _, _, _ = env.step(0)\n", + "starting_host.software_manager.show()\n", + "\n", + "env.step(0);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | RECONNAISSANCE\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|1|RECONNAISSANCE|*No Direct Impact*|TAP003 is identifying Sensitive systems, data and access control mechanisms in legitimate ways.|\n", + "\n", + "Currently, this stage in the kill chain is implemented via the 'DONOTHING' CAOS action." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.RECONNAISSANCE):\n", + " reconnaissance_obs_impact, _, _, _, _ = env.step(0)\n", + "\n", + "current_host = env.game.simulation.network.get_node_by_hostname(tap003.current_host)\n", + "current_host.software_manager.show()\n", + "display_obs_diffs(default_obs, reconnaissance_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | PLANNING\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|2|PLANNING| *No Direct Impact*| HOST:APPLICATION|TAP003 is devising a plan to exploit their elevated privileges.|\n", + "\n", + "Currently, the TAP003 agent does not perform any actions at this stage of the kill chain." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "pprint(\"---Starting Client State----\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.PLANNING):\n", + " planning_obs_impact, _, _, _, info = env.step(0);\n", + "current_host.software_manager.show()\n", + "display_obs_diffs(reconnaissance_obs_impact, planning_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | ACCESS\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|3|Access|_DONOTHING CAOS Action_|TAP003 uses their legitimate credentials to access the access control settings.|\n", + "\n", + "Currently, at this point of the kill chain stage the TAP003 does not perform any simulations actions. Future versions of TAP003 aim to leverage more of the simulation to create and remove accounts at this stage." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "pprint(\"---Starting Client State----\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.ACCESS):\n", + " access_obs_impact, _, _, _, info = env.step(0)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Compare observation space before and after ACCESS stage.\n", + "display_obs_diffs(planning_obs_impact, access_obs_impact, env.game.step_counter)\n", + "\n", + "starting_host.software_manager.show()\n", + "print_agent_actions_except_do_nothing(tap003.config.ref)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | MANIPULATION\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|4|Manipulation| **Target Host(s)** HOST::SESSIONS:REMOTE |TAP003 exploits their insider knowledge/privilege to implement changes for sabotage.|\n", + "\n", + "In the ``MANIPULATION`` kill chain stage the TAP003 agent creates new and alters pre-existing user accounts based on user given settings. \n", + "\n", + "_For more information please refer to the later TAP003 customisation section of the notebook._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "access_obs_impact, _, _, _, _ = env.step(0)\n", + "pprint(f\"Agent action: {info['agent_actions']['attacker']}\")\n", + "\n", + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "pprint(\"---Target Client State----\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.MANIPULATION):\n", + " manipulation_obs_impact, _, _, _, info = env.step(0)\n", + "\n", + "\n", + "# Example host\n", + "rem_pub_rt_dr.user_manager.show()\n", + "# rem_pub_rt_dr.user_session_manager.show()\n", + "\n", + "# Compare observation space before and after MANIPULATION stage.\n", + "display_obs_diffs(access_obs_impact, manipulation_obs_impact, env.game.step_counter)\n", + "print_agent_actions_except_do_nothing(tap003.config.ref)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | EXPLOITATION\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|5|EXPLOITATION| **Target Host(s)** ROUTER_ACL_ADDRULE |TAP003 exploiting their insider knowledge/privilege to implement changes for sabotage|\n", + "\n", + "In the final attack stage the TAP003 agent insert a malicious ACL rule(s) which blocks specific traffic, therefore triggering negative rewards for the green agents.\n", + "\n", + "The table below shows the impacts:\n", + "\n", + "|Target Router | Impact |\n", + "|----------------------|--------|\n", + "|`ST-INTRA-PRV-RT-DR-1`| Blocks all `POSTGRES_SERVER` that arrives at the `ST-INTRA-PRV-RT-DR-1` router. This rule will prevent all ST_PROJ_* hosts from accessing the database (`ST-DATA-PRV-SRV-DB`).|\n", + "|`ST-INTRA-PRV-RT-CR`| Blocks all `HTTP` traffic that arrives at the`ST-INTRA-PRV-RT-CR` router. This rule will prevent all SOME_TECH hosts from accessing the webserver (`ST-DMZ-PUB-SRV-WEB`)|\n", + "|`REM-PUB-RT-DR`| Blocks all `DNS` traffic that arrives at the `REM-PUB-RT-DR` router. This rule prevents any remote site works from accessing the DNS Server (`ISP-PUB-SRV-DNS`).|\n", + "\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "_(Click to enlarge)_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "manipulation_obs_impact, _, _, _, _ = env.step(0)\n", + "pprint(f\"Agent action: {info['agent_actions']['attacker']}\")\n", + "\n", + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "pprint(\"---Target Client State----\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.EXPLOIT):\n", + " exploit_obs_impact, _, _, _, info = env.step(0)\n", + " \n", + "st_intra_prv_rt_cr.acl.show()\n", + "st_intra_prv_rt_dr_1.acl.show()\n", + "rem_pub_rt_dr.acl.show()\n", + "\n", + "# Stepping once more to allow the malicious acl to appear in the simulation.\n", + "exploit_obs_impact, _, _, _, _ = env.step(0)\n", + "\n", + "# Compare observation space before and after MANIPULATION stage.\n", + "display_obs_diffs(manipulation_obs_impact, exploit_obs_impact, env.game.step_counter)\n", + "print_agent_actions_except_do_nothing(tap003.config.ref)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Demonstration of the ACL Rule blocking traffic\n", + "\n", + "As an example of the malicious ACL Rule affecting traffic across the network, attempting to query the database server (ST-DATA-PRV-SRV-DB) from any of the `ST_PROJECT_*` networks should fail because it is must route through the `ST-INTRA-PRV-RT-DR-1` router which TAP003 has configured to block all `POSTGRES_SERVER` traffic." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "affected_node = env.game.simulation.network.get_node_by_hostname(\"ST-PROJ-A-PRV-PC-1\")\n", + "database_server = env.game.simulation.network.get_node_by_hostname(\"ST-DATA-PRV-SRV-DB\")\n", + "\n", + "st_intra_prv_rt_dr_1 = env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-DR-1\")\n", + "\n", + "database_client: DatabaseClient = affected_node.software_manager.software.get(\"database-client\")\n", + "st_intra_prv_rt_dr_1.acl.show()\n", + "\n", + "# should be False\n", + "print(database_client.query(sql=\"SELECT\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, ACL rules will not stop ICMP traffic, allowing us to still ping the target nodes as shown below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# should be true\n", + "print(affected_node.ping(database_server.network_interface[1].ip_address))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, by default, TAP003 is configured to add two more ACLs on the UC7 network as shown in the table previously." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This ACL blocks all web_server traffic from within the main `SOME_TECH` office. Thus leading to every SOME_TECH host unable to access the web-server\n", + "st_intra_prv_rt_cr = env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_cr.acl.show()\n", + "\n", + "st_head_office_private_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"ST-HO-PRV-PC-1\")\n", + "st_head_office_private_pc_1_web_browser: WebBrowser = st_head_office_private_pc_1.software_manager.software[\"web-browser\"]\n", + "st_head_office_private_pc_1_web_browser.get_webpage(url=\"http://some_tech.com\")\n", + "st_head_office_private_pc_1_web_browser.sys_log.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This ACL blocks all DNS traffic from exiting the SOME_TECH remote site.\n", + "rem_pub_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(hostname=\"REM-PUB-PC-1\")\n", + "rem_pub_rt_dr = env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\")\n", + "dns_client: DNSClient = rem_pub_pc_1.software_manager.software[\"dns-client\"]\n", + "dns_client.dns_cache.clear()\n", + "dns_client.check_domain_exists(target_domain=\"some_tech.com\")\n", + "rem_pub_rt_dr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Kill Chain Configurations** | Overview:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following sections cover the TAP003's settings & configurations. \n", + "Each section contains code cells which directly demonstrate each option and it's effect on TAP003." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Threat Actor Profile (TAP) wide Agent Settings\n", + "\n", + " Threat Actor Profile Agent Settings \n", + "\n", + "
\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|start_step|The initial kill chain starting step.|Int|_Required_|\n", + "|frequency|The frequency of each kill chains starting steps.|Int|_Required_|\n", + "|variance| The timestep variance between frequency|Int|_Required_|\n", + "|repeat_kill_chain|Indicates whether the attack is repeated throughout the episode.|Bool|_Required_|\n", + "|repeat_kill_chain_stages|Indicates if the kill_chain stage should reset upon failure or retry.|Bool|_Required_|\n", + "|default_target_node|The Target Host|Str|_Required_|\n", + "|starting_nodes|A list of Potential Targets|List|_Optional_|\n", + "|kill_chain|_See the next notebook section_|Dict|_Required_||\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_Example kill chain setting configuration_\n", + "```yaml\n", + " start_step: 1\n", + " frequency: 3\n", + " variance: 0\n", + " repeat_kill_chain: false\n", + " repeat_kill_chain_stages: true\n", + " default_starting_node: \"ST-PROJ-A-PRV-PC-1\"\n", + " starting_nodes:\n", + " # starting_nodes: [\"ST-PROJ-A-PRV-PC-1\", \"ST-PROJ-B-PRV-PC-2\", \"ST-PROJ-C-PRV-PC-3\"]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Kill Chain Settings Overview\n", + "Currently, each kill chain stage has a probability of success option.\n", + "Additionally the manipulation kill stage offers an additional configuration of the malicious ACL.\n", + "\n", + " TAP003's Kill Chain Settings \n", + "\n", + "
\n", + "\n", + "Kill Chain Stage 1 - _PLANNING_\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Probability Of Success - The chance of successfully carrying out this stage in the kill_chain.|float|_Required_|\n", + "|starting_network_knowledge| User Account details | dict |_required_|\n", + "\n", + "Kill Chain Stage 2 - _ACCESS_\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Probability Of Success - The chance of successfully carrying out this stage in the kill_chain.|float|_Required_|\n", + "\n", + "Kill Chain Stage 3 - _MANIPULATION_\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Probability Of Success - The chance of successfully carrying out this stage in the kill_chain.|float|_Required_|\n", + "|account_changes| User Accounts hijacked - Must be the same targets as `malicious_acl` used in next kill chain stage | dict | _Required_|\n", + "\n", + "Kill Chain Stage 4 - _EXPLOIT_\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|malicious_acl|The configurable ACL that the TAP003 agent adds to the target node.|dict|_Required_|\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_An example of a kill chain setting configuration_ \n", + "```yaml\n", + " kill_chain:\n", + " PLANNING:\n", + " probability: 1\n", + " starting_network_knowledge:\n", + " credentials:\n", + " ST-PROJ-A-PRV-PC-1:\n", + " username: admin\n", + " password: admin\n", + " ST-PROJ-B-PRV-PC-2:\n", + " username: admin\n", + " password: admin\n", + " ST-PROJ-C-PRV-PC-3:\n", + " username: admin\n", + " password: admin\n", + " ST-INTRA-PRV-RT-DR-1:\n", + " ip_address: 192.168.230.1\n", + " username: admin\n", + " password: admin\n", + " ST-INTRA-PRV-RT-CR:\n", + " ip_address: 192.168.160.1\n", + " username: admin\n", + " password: admin\n", + " REM-PUB-RT-DR:\n", + " ip_address: 192.168.10.2\n", + " username: admin\n", + " password: admin\n", + " ACCESS:\n", + " probability: 1\n", + " MANIPULATION:\n", + " probability: 1\n", + " account_changes:\n", + " - host: ST-INTRA-PRV-RT-DR-1\n", + " ip_address: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1\n", + " action: change_password\n", + " username: admin\n", + " new_password: \"red_pass\"\n", + " - host: ST-INTRA-PRV-RT-CR\n", + " ip_address: 192.168.160.1 # ST-INTRA-PRV-RT-CR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " - host: REM-PUB-RT-DR\n", + " ip_address: 192.168.10.2 # REM-PUB-RT-DR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " EXPLOIT:\n", + " probability: 1\n", + " malicious_acls:\n", + " - target_router: ST-INTRA-PRV-RT-DR-1\n", + " ip_address: 192.168.230.1 \n", + " position: 1\n", + " permission: DENY\n", + " src_ip: ALL\n", + " src_wildcard_mask: 0.0.255.255\n", + " dst_ip: ALL\n", + " dst_wildcard_mask: 0.0.255.255\n", + " src_port: POSTGRES_SERVER\n", + " dst_port: POSTGRES_SERVER\n", + " protocol_name: TCP\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Configuration Examples \n", + "The following notebook section demonstrates a few potential examples of different TAP003 configurations and their expected behaviour within an episode." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "tap003 = env.game.agents['attacker']\n", + "for key,value in tap003.config.agent_settings:\n", + " if key == 'kill_chain':\n", + " pass\n", + " else:\n", + " print(f\"{key} : {value}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Start Step, Frequency & Variance\n", + "\n", + "In this section, the code cells below demonstrate the following options:\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|start_step|The initial kill chain starting step.|Int|_Required_|\n", + "|frequency|The frequency of each kill chains starting steps.|Str|_Required_|\n", + "|variance|The timestep variance between frequency.|Str|_Required_|\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below alters the start_step, frequency and variance to 20, 10 and 5 respectively.\n", + "Additionally, the probability of the kill chain stages are set to guarantee success. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['agents'][32]['agent_settings']['start_step'] = 20\n", + " cfg['agents'][32]['agent_settings']['frequency'] = 10\n", + " cfg['agents'][32]['agent_settings']['variance'] = 5\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['MANIPULATION']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['ACCESS']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['probability'] = 1\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The cell below demonstrates how the frequency & variance options affect TAP003's next execution step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "tap003 = env.game.agents['attacker']\n", + "print(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | repeat_kill_chain & repeat_kill_chain_stages\n", + "\n", + "This section demonstrate how the different repeat options may affect a 100 step length episode.\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|repeat_kill_chain|Indicates whether the attack is repeated throughout the episode.|Str|_Required_|\n", + "|repeat_kill_chain_stages|Indicates if the kill_chain stage should reset upon failure or retry.|Str|_Required_|\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = False\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = True\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = True\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "while(env.game.step_counter != 100):\n", + " env.step(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Starting Node\n", + "\n", + "The code cells below demonstrate how to configure TAP003's different options for selecting starting hosts.\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|default_starting_node|The default starting host.|Str|_Required_|\n", + "|starting_nodes|A list of potential targets|List|_Optional_|\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg[\"agents\"][32][\"agent_settings\"][\"default_starting_node\"] = \"ST-PROJ-A-PRV-PC-1\"\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "tap003 = env.game.agents[\"attacker\"]\n", + "print(f\"TA003's Selected Starting Host: {tap003.starting_node}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below demonstrates how starting node lists overrule the default start node options:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg[\"agents\"][32][\"agent_settings\"][\"default_starting_node\"] = \"ST-PROJ-A-PRV-PC-1\"\n", + " cfg[\"agents\"][32][\"agent_settings\"][\"starting_nodes\"] = [\"ST-PROJ-A-PRV-PC-1\",\"ST-PROJ-B-PRV-PC-2\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "env = PrimaiteGymEnv(env_config = cfg)\n", + "tap003 = env.game.agents[\"attacker\"]\n", + "print(f\"TA003's Selected Starting Host: {tap003.starting_node}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | probability\n", + "\n", + "The code cells below configure each kill chain's probability of success. \n", + "The cells also demonstrate how TAP003 handles failure within an episode.\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability | The chance of successfully carrying out this stage in the kill chain.|float|_Required_|\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below directly sets each probability of success to 0.25.
\n", + "Additionally the repeat kill chain and kill chain stage configuration options are set to false." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = False\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['probability'] = 0.25\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['ACCESS']['probability'] = 0.25\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['probability'] = 0.25\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "while(env.game.step_counter != 100):\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below performs the same episode but with the repeat_kill_chain and repeat_kill_chain_stages to true." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = True\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = True\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['MANIPULATION']['probability'] = 0.25\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['ACCESS']['probability'] = 0.25\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['probability'] = 0.25\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "while(env.game.step_counter != 100):\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | ACCESS - Starting Network Knowledge\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|probability |Action Probability - The chance of successfully carrying out this stage in the kill_chain.|str|_Required_|\n", + "|starting_network_knowledge|TAP003's starting knowledge. Used to login into the target accounts in the ``MANIPULATION`` stage.|dict|_Required_|" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``starting_network_knowledge``\n", + "\n", + "***\n", + "\n", + "The ``starting_network_knowledge`` options uses the following schema:\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|credentials|The credential knowledge does TAP003 starts the episode with.|dict|_Required_|\n", + "\n", + "```yaml\n", + " kill_chain:\n", + " PLANNING:\n", + " probability: 1\n", + " starting_network_knowledge:\n", + " credentials:\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``credentials``\n", + "\n", + "***\n", + "\n", + "The ``credentials`` options uses the following schema:\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|ip_address |The IP Address of the target host.|str|_Required_|\n", + "|username |A username of a valid user account. Defaults to ``Admin``.|str|_Optional_|\n", + "|password |The corresponding valid password of the given username. Defaults to ``Admin``.|str|_Optional_|\n", + "\n", + "These credentials will are used in the ``MANIPULATION`` kill chain stage to login into the target accounts. Therefore the ``credentials`` options needs contain the same hosts that the the ``ACCOUNT_CHANGES`` option\n", + "\n", + "```yaml\n", + "\n", + " kill_chain:\n", + " PLANNING:\n", + " probability: 1\n", + " starting_network_knowledge:\n", + " credentials:\n", + " ST-PROJ-A-PRV-PC-1:\n", + " username: admin\n", + " password: admin\n", + " ST-PROJ-B-PRV-PC-2:\n", + " username: admin\n", + " password: admin\n", + " ST-PROJ-C-PRV-PC-3:\n", + " username: admin\n", + " password: admin\n", + " ST-INTRA-PRV-RT-DR-1:\n", + " ip_address: 192.168.230.1\n", + " username: admin\n", + " password: admin\n", + " ST-INTRA-PRV-RT-CR:\n", + " ip_address: 192.168.160.1\n", + " username: admin\n", + " password: admin\n", + " REM-PUB-RT-DR:\n", + " ip_address: 192.168.10.2\n", + " username: admin\n", + " password: admin\n", + "\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "tap003 = env.game.agents['attacker']\n", + "# This while loop will run TAP003 until it hits TAP003 reaches ``ACCESS`` (Therefore passing PLANNING)\n", + "while(tap003.current_kill_chain_stage != InsiderKillChain.ACCESS):\n", + " env.step(0)\n", + "for account in tap003.network_knowledge['credentials']:\n", + " print(f\"----{account}---\")\n", + " for key in tap003.network_knowledge['credentials'][account]:\n", + " pprint(f\"{key}: {tap003.network_knowledge['credentials'][account][key]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | Manipulation - Account Changes\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Action Probability - The chance of successfully carrying out this stage in the kill_chain.|str|_Required_|\n", + "|account_changes|List of dictionaries containing the target hostnames, ip and account configuration changes.|list[dict]|_Required_|\n", + "\n", + "```yaml\n", + " kill_chain:\n", + " MANIPULATION:\n", + " probability: 1\n", + " account_changes:\n", + " - host: ST-INTRA-PRV-RT-DR-1\n", + " ip_address: 192.168.230.1 # ST-INTRA-PRV-RT-DR-1\n", + " action: change_password\n", + " username: admin\n", + " new_password: \"red_pass\"\n", + " - host: ST-INTRA-PRV-RT-CR\n", + " ip_address: 192.168.160.1 # ST-INTRA-PRV-RT-CR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " - host: REM-PUB-RT-DR\n", + " ip_address: 192.168.10.2 # REM-PUB-RT-DR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + "\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``account_changes``\n", + "\n", + "***\n", + "\n", + "The ``account_changes`` options uses the following schema:\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|host|The hostname of the target host.|str|_Required_|\n", + "|ip_address|The IP Address of the target host.|str|_Required_|\n", + "|action|The user account manager action. Currently only ``change_password`` is available.|str|_Required_|\n", + "|username|The username of the account that TAP003 will target.|str|_Required_|\n", + "|new_password|The new password that TAP003 will set.|str|_Required_|\n", + "\n", + "_For further information around the simulation implementation of user sessions please consult the ``user_session_manager`` primAITE API documentation page within PrimAITE's user guide documentation._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | Manipulation - Sandbox \n", + "\n", + "The code cells below can be used as a sandbox to trial out different configuration options and their impact on the simulation. Additionally, if users wish to alter an account other than admin on this sandbox they can alter the ``new_users`` dictionary in the code cell below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# New ST-PROJ-B-PRV-PC-2 user account\n", + "user_username = \"example_user_1\"\n", + "user_password = \"example_pass_1\"\n", + "user_admin = \"False\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "new_users = {\"username\": user_username, \"password\": user_password, \"is_admin\": user_admin }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Changing the password\n", + "tap003_new_password = \"tap003_password\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "account_changes = [{\"host\":\"ST-PROJ-A-PRV-PC-2\", \"ip_address\": \"192.168.230.2\", \"user_name\": user_username, \"old_password\": user_password, \"new_password\": tap003_new_password}]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we update the UC7 config and implement the new user accounts and run through an episode." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + " cfg['simulation']['network']['nodes'][31].update({'users':[]})\n", + " cfg['simulation']['network']['nodes'][31]['users'].append(new_users)\n", + " cfg['agents'][32]['agent_settings']['start_step'] = 1\n", + " cfg['agents'][32]['agent_settings']['frequency'] = 3\n", + " cfg['agents'][32]['agent_settings']['variance'] = 0\n", + " starting_creds = {\"username\": \"admin\",\"password\":\"admin\", \"ip_address\":\"192.168.230.2\"} # Adding \"ST-PROJ-A-PRV-PC-2\" to TAP003's starting_network_knowledge\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['starting_network_knowledge']['credentials'].update({\"ST-PROJ-A-PRV-PC-2\": starting_creds})\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['MANIPULATION']['account_changes'] = account_changes\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "while(env.game.step_counter != 90): # 20 Red Actions (frequency of 3)\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "target_host = env.game.simulation.network.get_node_by_hostname(\"ST-PROJ-A-PRV-PC-2\")\n", + "target_host.user_manager.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | Exploit - Malicious ACL\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Action Probability - The chance of successfully carrying out this stage in the kill_chain.|str|_Required_|\n", + "|malicious_acls|The configurable ACL that the TAP003 agent adds to the target node.|dict|_Required_|\n", + "\n", + "The malicious ACL is configured identically to the other ACLs. except from the target router/firewall. \n", + "This option is set to the TAP003's configured target host automatically.\n", + "\n", + "TAP003 intends to leverage these ACL's for malicious purposes. The default configuration is to deny all traffic from and towards the 0.0.0.255 subnet. \n", + "The configurability of the malicious ACL will allow for future releases of TAP type agents itself to perform more complex attacks. Currently, the TAP003 aims to block green agent traffic." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``malicious_acl``\n", + "\n", + "***\n", + "\n", + "The ``malicious_acl`` options uses the following schema:\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|target_router|The hostname of target router.|str|_Required_|\n", + "|ip_address|The ip_address of target router.|str|_Required_|\n", + "|position|The position that the malicious ACL is placed.|str|_Required_|\n", + "|permission|The implicit action. Should the ACL ``DENY`` or ``PERMIT``.|str|_Required_|\n", + "|src_ip|The source IP address to be targeted by the ``ACL``.|str|_Required_|\n", + "|src_wildcard_mask|The wildcard mask for the source ip address.|str|_Required_|\n", + "|dst_ip|The destination IP address to be targeted by the ``ACL``.|str|_Required_|\n", + "|dst_wildcard_mask|The wildcard mask for the destination ip address.|str|_Required_|\n", + "|src_port|The source port to be targeted by the ``ACL``.|str|_Required_|\n", + "|dst_port|The destination port to be targeted by the ``ACL``.|str|_Required_|\n", + "|protocol_name|The transport layer protocol_name to be targeted.|str|_Required_|\n", + "\n", + "_For further information please consult the simulation ACL documentation within PrimAITE's Sphinx documentation._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below demonstrates the default ACL's added by TAP003:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['start_step'] = 1\n", + " cfg['agents'][32]['agent_settings']['frequency'] = 3 # Install action takes multiple timesteps.\n", + " cfg['agents'][32]['agent_settings']['variance'] = 0\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['MANIPULATION']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['ACCESS']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['probability'] = 1\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "while(env.game.step_counter != 70):\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "for key,value in tap003.config.agent_settings:\n", + " if key != 'kill_chain':\n", + " pass\n", + " else:\n", + " for kill_chain_setting_key in value:\n", + " if kill_chain_setting_key == 'EXPLOIT':\n", + " pprint(value[kill_chain_setting_key]['malicious_acls'][0])\n", + " pprint(value[kill_chain_setting_key]['malicious_acls'][1])\n", + " pprint(value[kill_chain_setting_key]['malicious_acls'][2])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_dr_1 = env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-DR-1\")\n", + "st_intra_prv_rt_dr_1.acl.show()\n", + "\n", + "rem_pub_rt_dr = env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\")\n", + "rem_pub_rt_dr.acl.show()\n", + "\n", + "st_intra_prv_rt_cr = env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_cr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unlike the blue agent, TAP003 does not need to use it's action space options for indexing different options, meaning that ACL's are a lot easier to configure.\n", + "\n", + "The sandbox below can be used to try out different configuration options and their impact on the simulation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | Exploit - Sandbox " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "# please ensure that the source/dest ip_addresses are within the blue agent's observation space ip_list option.\n", + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + " cfg['agents'][32]['agent_settings']['start_step'] = 1\n", + " cfg['agents'][32]['agent_settings']['frequency'] = 3\n", + " cfg['agents'][32]['agent_settings']['variance'] = 0\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['position'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['permission'] = \"DENY\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['src_ip'] = \"ALL\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['src_wildcard'] = '0.0.255.255'\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['dst_ip'] = 'ALL'\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['dst_wildcard'] = \"0.0.255.255\"\n", + " # Please refer to the ``PORT`` class in the primAITE API for more information around the different supported ports\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['src_port'] = 'POSTGRES_SERVER'\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['dst_port'] = \"POSTGRES_SERVER\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['protocol_name'] = 'ALL'\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "while(env.game.step_counter != 50):\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "target_host = env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-DR-1\")\n", + "target_host.acl.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-Training.ipynb b/src/primaite/notebooks/UC7-Training.ipynb new file mode 100644 index 00000000..f15084ac --- /dev/null +++ b/src/primaite/notebooks/UC7-Training.ipynb @@ -0,0 +1,156 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "# Training an SB3 Agent\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n", + "\n", + "This notebook will demonstrate how to use primaite to create and train a PPO agent, using a pre-defined configuration file." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### First, we import the inital packages and read in our configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite import PRIMAITE_PATHS\n", + "from prettytable import PrettyTable\n", + "from deepdiff.diff import DeepDiff\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "\n", + "scenario_path = PRIMAITE_PATHS.user_config_path / \"example_config/uc7_config.yaml\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gym = PrimaiteGymEnv(env_config=scenario_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stable_baselines3 import PPO\n", + "\n", + "# EPISODE_LEN = 128\n", + "EPISODE_LEN = 128\n", + "NUM_EPISODES = 10\n", + "NO_STEPS = EPISODE_LEN * NUM_EPISODES\n", + "BATCH_SIZE = 32\n", + "LEARNING_RATE = 3e-4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = PPO('MlpPolicy', gym, learning_rate=LEARNING_RATE, n_steps=NO_STEPS, batch_size=BATCH_SIZE, verbose=0, tensorboard_log=\"./PPO_UC7/\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.learn(total_timesteps=NO_STEPS)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.save(\"PrimAITE-PPO-UC7-example-agent\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "eval_model = PPO(\"MlpPolicy\", gym)\n", + "eval_model = PPO.load(\"PrimAITE-PPO-UC7-example-agent\", gym)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stable_baselines3.common.evaluation import evaluate_policy\n", + "\n", + "evaluate_policy(eval_model, gym, n_eval_episodes=1)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-attack-variants.ipynb b/src/primaite/notebooks/UC7-attack-variants.ipynb new file mode 100644 index 00000000..01888134 --- /dev/null +++ b/src/primaite/notebooks/UC7-attack-variants.ipynb @@ -0,0 +1,586 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# UC7 with Attack Variability\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "This notebook demonstrates the PrimAITE environment with the UC7 network laydown and multiple attack personas. The first attack persona is TAP001 which performs a ransomware attack against the database. The other one is TAP003 which is able to maliciously add ACL rules that block green pattern of life.\n", + "\n", + "The environment switches between these two attacks on a pre-defined schedule which is defined in the schedule.yaml file of the scenario folder." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup and Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite import PRIMAITE_PATHS\n", + "from prettytable import PrettyTable\n", + "from deepdiff.diff import DeepDiff\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.system.services.dns.dns_server import DNSServer\n", + "from primaite.simulator.system.software import SoftwareHealthState\n", + "from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus\n", + "from primaite.simulator.network.hardware.nodes.network.switch import Switch\n", + "from primaite.simulator.system.applications.web_browser import WebBrowser\n", + "from primaite.simulator.network.container import Network\n", + "from primaite.simulator.system.services.service import ServiceOperatingState\n", + "from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState\n", + "from primaite.simulator.system.services.database.database_service import DatabaseService\n", + "from primaite.simulator.system.applications.database_client import DatabaseClient\n", + "from primaite.simulator.network.hardware.nodes.network.firewall import Firewall\n", + "from primaite.game.game import PrimaiteGame\n", + "from primaite.simulator.sim_container import Simulation\n", + "from primaite.config.load import load, _EXAMPLE_CFG\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "\n", + "scenario_path = PRIMAITE_PATHS.user_config_path / \"example_config/uc7_multiple_attack_variants\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = PrimaiteGymEnv(env_config=scenario_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Schedule\n", + "\n", + "Let's print the schedule so that we can see which attack we can expect on each episode.\n", + "\n", + "On episodes 0-4, the TAP001 agent will be used, and on episodes 5-9, the TAP003 agent will be used. Then, the environment will alternate between the two. Furthermore, the TAP001 agent will alternate between starting at `ST-PROJ-A-PRV-PC-1`, `ST-PROJ-B-PRV-PC-2`, `ST-PROJ-C-PRV-PC-3`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(scenario_path / \"schedule.yaml\",'r') as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TAP001 attack\n", + "\n", + "Let's first demonstrate the TAP001 attack. We will let the environment run for 30 steps and print out the red agent's actions.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#utils\n", + "def run_green_and_red_pol(num_steps):\n", + " for i in range(num_steps): # perform steps\n", + " env.step(0)\n", + "\n", + "def print_agent_actions_except_do_nothing(agent_name):\n", + " \"\"\"Get the agent's action history, filter out DONOTHING actions, print relevant data in a table.\"\"\"\n", + " table = PrettyTable()\n", + " table.field_names = [\"Step\", \"Action\", \"Node\", \"Application\", \"Target IP\", \"Response\"]\n", + " print(f\"Episode: {env.episode_counter}, Actions for '{agent_name}':\")\n", + " for item in env.game.agents[agent_name].history:\n", + " if item.action == \"do-nothing\":\n", + " continue\n", + "\n", + " node, application, target_ip = \"N/A\", \"N/A\", \"N/A\",\n", + "\n", + " if item.action.startswith(\"node-map\"):\n", + " node = item.parameters['source-node']\n", + " application = \"nmap\"\n", + " target_ip = str(item.parameters['target_ip_address'])\n", + " target_ip = (target_ip[:25]+'...') if len(target_ip)>25 else target_ip # truncate long string\n", + "\n", + " elif item.action == \"router-acl-add-rule\":\n", + " node = item.parameters.get(\"router_name\")\n", + " elif item.action == \"node-send-remote-command\":\n", + " node = item.parameters.get(\"node_name\")\n", + " target_ip = item.parameters.get(\"remote_ip\")\n", + " application = item.parameters.get(\"command\")\n", + " elif item.action == \"node-session-remote-login\":\n", + " node = item.parameters.get(\"node_name\")\n", + " target_ip = item.parameters.get(\"remote_ip\")\n", + " application = \"user-manager\"\n", + " elif item.action.startswith(\"c2-server\"):\n", + " application = \"c2-server\"\n", + " node = item.parameters.get('node_name')\n", + " elif item.action == \"configure-c2-beacon\":\n", + " application = \"c2-beacon\"\n", + " node = item.parameters.get('node_name')\n", + "\n", + " else:\n", + " if (node_id := item.parameters.get('node_id')) is not None:\n", + " node = env.game.agents[agent_name].action_manager.node_names[node_id]\n", + " if (application_id := item.parameters.get('application_id')) is not None:\n", + " application = env.game.agents[agent_name].action_manager.application_names[node_id][application_id]\n", + " if (application_name := item.parameters.get('application_name')) is not None:\n", + " application = application_name\n", + "\n", + " table.add_row([item.timestep, item.action, node, application, target_ip, item.response.status])\n", + "\n", + " print(table)\n", + " print(\"(Any DONOTHING actions are omitted)\")\n", + "\n", + "def finish_episode_and_print_reward():\n", + " while env.game.step_counter < 128:\n", + " env.step(0)\n", + " print(f\"Total reward this episode: {env.agent.reward_function.total_reward:2f}\")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "run_green_and_red_pol(110)\n", + "print_agent_actions_except_do_nothing(\"attacker\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_data_prv_srv_db: Server = env.game.simulation.network.get_node_by_hostname(\"ST-DATA-PRV-SRV-DB\")\n", + "st_data_prv_srv_db.file_system.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "finish_episode_and_print_reward()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TAP001 Prevention" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The blue agent should be able to prevent the ransomware attack by blocking the red agent's access to the database. Let's run the environment until the observation space shows symptoms of the attack starting.\n", + "\n", + "Because we are in episode index 1, the red agent will use `ST-PROJ-A-PRV-PC-1` to start the attack. On step 25, the red agent installs `RansomwareScript`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "obs, reward, term, trunc, info = env.step(0)\n", + "for i in range(25): # we know that the ransomware install happens at step 25\n", + " old = obs\n", + " obs, reward, term, trunc, info = env.step(0)\n", + " new = obs\n", + "\n", + "diff = DeepDiff(old,new)\n", + "print(f\"Step {env.game.step_counter}\") # it's step 26 now because the step counter is incremented after the step\n", + "for d,v in diff.get('values_changed', {}).items():\n", + " print(f\"{d}: {v['old_value']} -> {v['new_value']}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that on HOST0, application index 1 has gone from `operating_status` 0 to 3, meaning there wasn't an application before, but now there is an application in the `INSTALLING` state. The blue agent should be able to detect this and block the red agent's access to the database. Action 43 will block `ST-PROJ-A-PRV-PC-1` from sending POSTGRES traffic to the DB server.\n", + "\n", + "If this were a different episode, it could have been `ST-PROJ-B-PRV-PC-2` or `ST-PROJ-C-PRV-PC-3` that are affected, and a different defensive action would be required." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(43)\n", + "env.step(45)\n", + "env.step(47)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr: Router = env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_cr.acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "finish_episode_and_print_reward()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now TAP001 is unable to locate the database!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print_agent_actions_except_do_nothing(\"attacker\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TAP003 attack\n", + "\n", + "Let's skip until episode 5 and demonstrate the TAP003 attack. We will let the environment run and print out the red agent's actions.\n", + "\n", + "By default, TAP003 will add the following rules:\n", + "\n", + "|Target Router | Impact |\n", + "|----------------------|--------|\n", + "|`ST-INTRA-PRV-RT-DR-1`| Blocks all `POSTGRES_SERVER` that arrives at the `ST-INTRA-PRV-RT-DR-1` router. This rule will prevent all ST_PROJ_* hosts from accessing the database (`ST-DATA-PRV-SRV-DB`).|\n", + "|`ST-INTRA-PRV-RT-CR`| Blocks all `HTTP` traffic that arrives at the`ST-INTRA-PRV-RT-CR` router. This rule will prevent all SOME_TECH hosts from accessing the webserver (`ST-DMZ-PUB-SRV-WEB`)|\n", + "|`REM-PUB-RT-DR`| Blocks all `DNS` traffic that arrives at the `REM-PUB-RT-DR` router. This rule prevents any remote site works from accessing the DNS Server (`ISP-PUB-SRV-DNS`).|" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "while env.episode_counter < 5:\n", + " env.reset()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "run_green_and_red_pol(128)\n", + "print_agent_actions_except_do_nothing(\"attacker\")\n", + "obs, reward, term, trunc, info = env.step(0); # one more step so we can capture the value of `obs`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The agent selected to add ACL rules that will prevent green pattern of life by blocking a variety of different traffic. This has a negative impact on reward. Let's view the ACL list on the affected router." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-DR-1\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-CR\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\").acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that at indices 1-5, there are ACL rules that block all traffic. The blue agent can see this rule in the `ROUTERS` part of the observation space.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs['NODES']['ROUTER0']['ACL'][1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs['NODES']['ROUTER1']['ACL'][1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs['NODES']['ROUTER2']['ACL'][1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preventing TAP003 attack\n", + "\n", + "The blue agent can prevent the red agent from adding ACL rules. TAP003 relies on connecting to the router via SSH, and sending remote ACL_ADDRULE requests. The blue agent can prevent this by pre-emptively changing the admin password on the affected routers or by blocking SSH traffic between the red agent's starting node and the target routers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "obs, reward, term, trunc, info = env.step(0)\n", + "old = obs\n", + "for i in range(128): \n", + " obs, reward, term, trunc, info = env.step(0)\n", + " new = obs\n", + "\n", + "diff = DeepDiff(old,new)\n", + "print(f\"Step {env.game.step_counter}\") # it's the next step now because the step counter is incremented after the step\n", + "for d,v in diff.get('values_changed', {}).items():\n", + " print(f\"{d}: {v['old_value']} -> {v['new_value']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By printing the reward of each individual agent, we will see what green agents are affected the most. Of course, these green rewards count towards the blue reward so ultimately the blue agent should learn to remove the ACL rule." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "finish_episode_and_print_reward()\n", + "\n", + "for ag in env.game.agents.values():\n", + " print(ag.config.ref, ag.reward_function.total_reward)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The most effective option that the blue agent has against TAP003 is to prevent the red agent from ever adding the ACLs in the first place through blocking the SSH connection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "env.step(51) # SSH Blocking ACL on ST-INRA-PRV-RT-R1\n", + "finish_episode_and_print_reward()\n", + "\n", + "for ag in env.game.agents.values():\n", + " print(ag.config.ref, ag.reward_function.total_reward)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, another option the blue agent can take is to change the passwords of the different target routers that TAP003 will attack through the `NODE_ACCOUNTS_CHANGE_PASSWORD` action." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "env.step(50) # NODE_ACCOUNTS_CHANGE_PASSWORD | st-intra-prv-rt-cr\n", + "env.step(52) # NODE_ACCOUNTS_CHANGE_PASSWORD | st-intra-prv-rt-dr-1\n", + "env.step(54) # NODE_ACCOUNTS_CHANGE_PASSWORD | rem-pub-rt-dr\n", + "finish_episode_and_print_reward()\n", + "\n", + "for ag in env.game.agents.values():\n", + " print(ag.config.ref, ag.reward_function.total_reward)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, the blue agent can remedy the impacts of TAP003 through removing the malicious ACLs that TAP003 adds." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "\n", + "# Allow TAP003 to add it's malicious rules\n", + "for _ in range(45):\n", + " env.step(0)\n", + "\n", + "env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-CR\").acl.show()\n", + "env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-DR-1\").acl.show()\n", + "env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(44) # ROUTER_ACL_REMOVERULE | st-intra-prv-rt-cr\n", + "env.step(53) # ROUTER_ACL_REMOVERULE | st-intra-prv-rt-dr-1\n", + "env.step(55) # ROUTER_ACL_REMOVERULE | rem-pub-rt-dr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-CR\").acl.show()\n", + "env.game.simulation.network.get_node_by_hostname(\"ST-INTRA-PRV-RT-DR-1\").acl.show()\n", + "env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\").acl.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "finish_episode_and_print_reward()\n", + "\n", + "for ag in env.game.agents.values():\n", + " print(ag.config.ref, ag.reward_function.total_reward)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-network_connectivity.ipynb b/src/primaite/notebooks/UC7-network_connectivity.ipynb new file mode 100644 index 00000000..356e23aa --- /dev/null +++ b/src/primaite/notebooks/UC7-network_connectivity.ipynb @@ -0,0 +1,1141 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# UC7 Demonstration\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Network Description\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.network.hardware.nodes.network.switch import Switch\n", + "from primaite.simulator.network.container import Network\n", + "from primaite.simulator.network.hardware.nodes.network.firewall import Firewall\n", + "from primaite.game.game import PrimaiteGame\n", + "from primaite.simulator.sim_container import Simulation\n", + "import yaml\n", + "from pprint import pprint\n", + "from primaite.config.load import load, _EXAMPLE_CFG" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "use_case_7_config = load(_EXAMPLE_CFG/\"uc7_config.yaml\")\n", + "env = PrimaiteGymEnv(env_config=use_case_7_config)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "uc7_network = env.game.simulation.network\n", + "uc7_network.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Home Office Subnet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-PC-1\")\n", + "home_pub_pc_2: Computer = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-PC-2\")\n", + "home_pub_pc_srv: Server = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-SRV\")\n", + "home_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-RT-DR\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME PC 1 --> HOME PC 2\n", + "home_pub_pc_1.ping(home_pub_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME PC 2 --> HOME SERVER\n", + "home_pub_pc_2.ping(home_pub_pc_srv.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME SERVER --> HOME ROUTER\n", + "home_pub_pc_srv.ping(home_pub_rt_dr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Internet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isp_pub_rt_br: Router = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-RT-BR\")\n", + "isp_pub_srv_dns: Server = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-SRV-DNS\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_pc_1.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME ROUTER --> INTERNET ROUTER\n", + "home_pub_rt_dr.ping(isp_pub_rt_br.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_rt_dr.route_table.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME ROUTER --> INTERNET ISP\n", + "home_pub_rt_dr.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# INTERNET ROUTER --> INTERNET DNS\n", + "isp_pub_rt_br.ping(target_ip_address=isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME ROUTER --> INTERNET DNS\n", + "home_pub_rt_dr.ping(target_ip_address=isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# PC 1 --> INTERNET DNS\n", + "\n", + "home_pub_pc_1.ping(target_ip_address=isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isp_pub_rt_br.show_nic()\n", + "isp_pub_rt_br.ping(\"192.168.1.2\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Remote Site Subnet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "network: Network = env.game.simulation.network\n", + "\n", + "\n", + "rem_pub_fw: Firewall = network.get_node_by_hostname(hostname=\"REM-PUB-FW\")\n", + "rem_pub_rt_dr: Router = network.get_node_by_hostname(hostname=\"REM-PUB-RT-DR\")\n", + "rem_pub_sw_as: Switch = network.get_node_by_hostname(hostname=\"REM-PUB-SW-AS\")\n", + "rem_pub_pc_1: Computer = network.get_node_by_hostname(hostname=\"REM-PUB-PC-1\")\n", + "rem_pub_pc_2: Computer = network.get_node_by_hostname(hostname=\"REM-PUB-PC-2\")\n", + "rem_pub_srv: Computer = network.get_node_by_hostname(hostname=\"REM-PUB-SRV\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote PC 1 --> Remote PC_2\n", + "\n", + "rem_pub_pc_1.ping(rem_pub_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote PC 2 --> Remote Server\n", + "\n", + "rem_pub_pc_2.ping(rem_pub_srv.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Server --> Remote Site Router\n", + "\n", + "rem_pub_srv.ping(rem_pub_rt_dr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Site Router --> Remote Pub Firewall (Internal)\n", + "\n", + "rem_pub_rt_dr.ping(rem_pub_fw.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Site Router --> Remote Pub Firewall (External)\n", + "\n", + "rem_pub_rt_dr.ping(rem_pub_fw.network_interface[2].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Pub Firewall (external) --> Public Internet\n", + "\n", + "rem_pub_fw.ping(isp_pub_rt_br.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Site PC 1 --> Public DNS\n", + "\n", + "rem_pub_pc_1.ping(target_ip_address=isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Site PC 1 --> Home Office PC 1\n", + "\n", + "rem_pub_pc_1.ping(target_ip_address=home_pub_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | SOME_TECH_DMZ" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_pub_fw: Firewall = network.get_node_by_hostname(hostname=\"ST-PUB-FW\")\n", + "st_dmz_pub_srv_web: Server = network.get_node_by_hostname(hostname=\"ST-DMZ-PUB-SRV-WEB\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Firewall --> Some Tech DMZ public web server\n", + "\n", + "st_pub_fw.ping(st_dmz_pub_srv_web.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Firewall --> Public DNS Server\n", + "\n", + "st_pub_fw.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech DMZ public web serv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Firewall --> Internet Router (Port 4)\n", + "\n", + "st_pub_fw.ping(isp_pub_rt_br.network_interface[4].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | SOME_TECH_INTRANET" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr: Router = network.get_node_by_hostname(hostname=\"ST-INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_dr_1: Router = network.get_node_by_hostname(hostname=\"ST-INTRA-PRV-RT-DR-1\")\n", + "st_intra_prv_rt_dr_2: Router = network.get_node_by_hostname(hostname=\"ST-INTRA-PRV-RT-DR-2\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Intranet Private Router CR --> Some Tech Public Firewall\n", + "\n", + "st_intra_prv_rt_cr.ping(st_pub_fw.network_interface[3].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some Tech Intranet Router CR\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_intra_prv_rt_cr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 2 --> Some Tech Intranet Router CR\n", + "\n", + "st_intra_prv_rt_dr_2.ping(st_intra_prv_rt_cr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some tech intranet router DR 2\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_intra_prv_rt_dr_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some Tech Firewall (Internal Port)\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_pub_fw.network_interface[3].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some Tech Firewall (DMZ Port)\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_pub_fw.network_interface[2].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some Tech Firewall (External Port)\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_pub_fw.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Intranet Router DR 1 --> Some Tech DMZ web-server\n", + "\n", + "st_intra_prv_rt_dr_1.ping(target_ip_address=st_dmz_pub_srv_web.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Public Internet\n", + "\n", + "st_intra_prv_rt_dr_1.ping(isp_pub_rt_br.network_interface[3].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Public DNS \n", + "\n", + "st_intra_prv_rt_dr_1.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Intranet Router DR 1 --> Home Office PC 1\n", + "\n", + "st_intra_prv_rt_dr_1.ping(target_ip_address=home_pub_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Intranet Router DR 1 --> Remote Site PC 1\n", + "\n", + "st_intra_prv_rt_dr_1.ping(target_ip_address=rem_pub_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Head Office" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_head_office_private_pc_1: Computer = network.get_node_by_hostname(\"ST-HO-PRV-PC-1\")\n", + "st_head_office_private_pc_2: Computer = network.get_node_by_hostname(\"ST-HO-PRV-PC-2\")\n", + "st_head_office_private_pc_3: Computer = network.get_node_by_hostname(\"ST-HO-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Home office PC 1 --> ST Home Office PC 2\n", + "\n", + "st_head_office_private_pc_1.ping(st_head_office_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Home office PC 3 --> ST Router DR 2 \n", + "\n", + "st_head_office_private_pc_1.ping(st_intra_prv_rt_dr_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Home office PC 1 --> Public DNS\n", + "\n", + "st_head_office_private_pc_1.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Human Resources" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_human_resources_private_pc_1: Computer = network.get_node_by_hostname(\"ST-HR-PRV-PC-1\")\n", + "st_human_resources_private_pc_2: Computer = network.get_node_by_hostname(\"ST-HR-PRV-PC-2\")\n", + "st_human_resources_private_pc_3: Computer = network.get_node_by_hostname(\"ST-HR-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Human Resources PC 1 --> ST Human Resources PC 2 \n", + "\n", + "st_human_resources_private_pc_1.ping(st_human_resources_private_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Human Resources PC 2 --> # ST Human Resources PC 3\n", + "\n", + "st_human_resources_private_pc_2.ping(st_human_resources_private_pc_3.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Human Resources PC 1 --> ST Intranet Router DR 2 \n", + "\n", + "st_human_resources_private_pc_1.ping(st_intra_prv_rt_dr_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Human Resources PC 1 --> Public DNS \n", + "\n", + "st_human_resources_private_pc_1.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Data (Database / Database Backup)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_data_private_server_database: Server = network.get_node_by_hostname(\"ST-DATA-PRV-SRV-DB\")\n", + "st_data_private_server_storage: Server = network.get_node_by_hostname(\"ST-DATA-PRV-SRV-STORAGE\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Data Private Database --> ST Data Private Storage\n", + "\n", + "st_data_private_server_database.ping(st_data_private_server_storage.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Data Private Storage --> Public DNS\n", + "\n", + "st_data_private_server_storage.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Project A " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1: Computer = network.get_node_by_hostname(\"ST-PROJ-A-PRV-PC-1\")\n", + "st_project_a_private_pc_2: Computer = network.get_node_by_hostname(\"ST-PROJ-A-PRV-PC-2\")\n", + "st_project_a_private_pc_3: Computer = network.get_node_by_hostname(\"ST-PROJ-A-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 1 --> ST Private Project A PC 2\n", + "\n", + "st_project_a_private_pc_1.ping(st_project_a_private_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 2 --> ST Private Project A PC 3\n", + "\n", + "st_project_a_private_pc_3.ping(st_project_a_private_pc_3.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 3 --> Public DNS\n", + "\n", + "st_project_a_private_pc_3.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 1 --> ST-INTRA-PRV-RT-CR\n", + "\n", + "st_project_a_private_pc_1.ping(st_intra_prv_rt_cr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 1 --> ST-INTRA-PRV-RT-DR-2\n", + "\n", + "st_project_a_private_pc_1.ping(\"192.168.170.2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Project B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_b_private_pc_1: Computer = network.get_node_by_hostname(\"ST-PROJ-B-PRV-PC-1\")\n", + "st_project_b_private_pc_2: Computer = network.get_node_by_hostname(\"ST-PROJ-B-PRV-PC-2\")\n", + "st_project_b_private_pc_3: Computer = network.get_node_by_hostname(\"ST-PROJ-B-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project B PC 1 --> ST Private Project B PC 2\n", + "\n", + "st_project_b_private_pc_1.ping(st_project_b_private_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project B PC 2 --> ST Private Project B PC 3\n", + "\n", + "st_project_b_private_pc_2.ping(st_project_b_private_pc_3.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project B PC 3 --> Public DNS\n", + "\n", + "st_project_b_private_pc_3.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Project C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_c_private_pc_1: Computer = network.get_node_by_hostname(\"ST-PROJ-C-PRV-PC-1\")\n", + "st_project_c_private_pc_2: Computer = network.get_node_by_hostname(\"ST-PROJ-C-PRV-PC-2\")\n", + "st_project_c_private_pc_3: Computer = network.get_node_by_hostname(\"ST-PROJ-C-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project C PC 1 --> ST Private Project C PC 2\n", + "\n", + "st_project_c_private_pc_1.ping(st_project_c_private_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project C PC 2 --> ST Private Project C PC 3\n", + "\n", + "st_project_c_private_pc_2.ping(st_project_c_private_pc_3.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project c PC 3 --> Public DNS\n", + "\n", + "st_project_c_private_pc_3.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_c_private_pc_1.ping(st_project_b_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_b_private_pc_1.ping(st_project_a_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1.ping(st_head_office_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1.ping(st_human_resources_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Applications & Services" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# DNS Server\n", + "\n", + "isp_pub_srv_dns.software_manager.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Web Server & Web Browser\n", + "\n", + "st_project_a_web_browser = st_project_a_private_pc_1.software_manager.software[\"web-browser\"]\n", + "st_project_a_web_browser.get_webpage()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_web_server = st_dmz_pub_srv_web.software_manager.software[\"web-server\"]\n", + "st_web_server.sys_log.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_database_client = st_project_a_private_pc_1.software_manager.software[\"database-client\"]\n", + "st_database_client.connect()\n", + "\n", + "st_database = st_data_private_server_database.software_manager.software[\"database-service\"]\n", + "st_database.sys_log.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_ftp_server = st_data_private_server_storage.software_manager.software[\"ftp-server\"]\n", + "\n", + "st_ftp_server.sys_log.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from primaite.simulator.system.services.dns.dns_client import DNSClient\n", + "home_pub_rt_dr.acl.show()\n", + "\n", + "home_pub_pc_1: Computer = network.get_node_by_hostname(\"HOME-PUB-PC-1\")\n", + "dns_client: DNSClient = home_pub_pc_1.software_manager.software[\"dns-client\"]\n", + "\n", + "dns_client.check_domain_exists(target_domain=\"some_tech.com\")\n", + "dns_client.dns_cache.get(\"some_tech.com\", None) is not None\n", + "len(dns_client.dns_cache) == 1\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# from primaite.simulator.system.applications.application import ApplicationOperatingState\n", + "# from primaite.simulator.system.applications.database_client import database-client\n", + "# from primaite.simulator.system.services.service import ServiceOperatingState\n", + "\n", + "# home_pub_pc_1_dns_client: dns-client = home_pub_pc_1.software_manager.software[\"dns-client\"]\n", + "# home_pub_pc_1_database_client: database-client = home_pub_pc_1.software_manager.software[\"database-client\"]\n", + "\n", + "\n", + "# home_pub_pc_1_dns_client.operating_state = ServiceOperatingState.PAUSED\n", + "# home_pub_pc_1_database_client.operating_state = ApplicationOperatingState.CLOSED" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for _ in range(80):\n", + " env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def print_agent_actions_except_do_nothing(agent_name):\n", + " print(f\"\\n{agent_name} actions this episode:\")\n", + " for item in env.game.agents[agent_name].history:\n", + " if item.action != \"DONOTHING\":\n", + " node, application = 'unknown', 'unknown'\n", + " if (node_id := item.parameters.get('node_id')) is not None:\n", + " node = env.game.agents[agent_name].action_manager.node_names[node_id]\n", + " if (application_id := item.parameters.get('application_id')) is not None:\n", + " application = env.game.agents[agent_name].action_manager.application_names[node_id][application_id]\n", + " print(f\"Step: {item.timestep}, action: {item.action}, {node}, {application}, response: {item.response.status}\")\n", + "\n", + "print_agent_actions_except_do_nothing(\"HOME_WORKER-1-DB\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "green_agent = env.game.agents.get(\"HOME_WORKER-1-DB\")\n", + "green_agent.reward_function.total_reward" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print_agent_actions_except_do_nothing(\"HOME_WORKER-1-WEB\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "green_agent = env.game.agents.get(\"HOME_WORKER-1-WEB\")\n", + "green_agent.reward_function.total_reward" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "red_agent = env.game.agents.get(\"attacker\")\n", + "red_agent.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1.file_system.show()\n", + "\n", + "# # st_project_a_private_pc_1.file_system.folders[\"exfiltration\"].show()\n", + "\n", + "# st_project_a_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr.acl.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/_package_data/uc7/blue_agent_action_and_obs.png b/src/primaite/notebooks/_package_data/uc7/blue_agent_action_and_obs.png new file mode 100644 index 00000000..85d5537d Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/blue_agent_action_and_obs.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_network.png b/src/primaite/notebooks/_package_data/uc7/uc7_network.png new file mode 100644 index 00000000..32cecb6a Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_network.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed.png b/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed.png new file mode 100644 index 00000000..c6d15435 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed_svg.svg b/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed_svg.svg new file mode 100644 index 00000000..8df869e7 --- /dev/null +++ b/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed_svg.svg @@ -0,0 +1,4 @@ + + + +



ST_PROJ_A-
PRV-PC-1
ST_PROJ_A-...
SOME TECH PROJECT A (ST_PROJ_A)
SOME TECH PROJECT A (ST_PROJ_A)
192.168.240.0/29
192.168.240.0/29
3
3
2
2
1
1
9
9





ST_INTRA-PRV-
RT-DR-1
ST_INTRA-PRV-...
ST_INTRA-PRV-
RT-CR
ST_INTRA-PRV-...
ST_INTRA-PRV-
RT-DR-2
ST_INTRA-PRV-...
Port 1: 192.168.170.2
Port 1: 192.168.170.2
Port 3: 192.168.170.1
Port 3: 192.168.170.1


Port 2: 192.168.160.1
Port 2: 192.168.160.1
Port 1: 192.168.160.2
Port 1: 192.168.160.2
SOME_TECH HUMAN RESOURCES
(ST_HR)
SOME_TECH HUMAN RESOURCES...
ST_PROJ_C-
PRV-SW-AS
ST_PROJ_C-...
ST_PROJ_C-
PRV-PC-3
ST_PROJ_C-...
3
3
2
2
1
1
9
9
Port 1: 192.168.250.4
Port 1: 192.168.250.4
Port 4
Port 4
ST_PROJ_C-
PRV-PC-1
ST_PROJ_C-...
3
3
2
2
1
1
9
9
Port 1: 192.168.250.2
Port 1: 192.168.250.2
Port 2
Port 2
ST_PROJ_C-
PRV-PC-2
ST_PROJ_C-...
3
3
2
2
1
1
9
9
Port 1: 192.168.250.3
Port 1: 192.168.250.3
Port 3
Port 3
SOME TECH PROJECT C (ST_PROJ_C)
SOME TECH PROJECT C (ST_PROJ_C)
192.168.250.0/29
192.168.250.0/29
192.168.160.0/30
192.168.160.0/30
192.168.170.0/30
192.168.170.0/30
192.168.210.0/29
192.168.210.0/29




Port 4: 10.1.100.1
Port 4: 10.1.100.1
External Port: 10.1.100.2
External Port: 10.1.100.2
ST_HO-PRV-PC-2
ST_HO-PRV-PC-2
3
3
2
2
1
1
9
9
ST_HO-PRV-PC-1
ST_HO-PRV-PC-1
3
3
2
2
1
1
9
9
Port 2
Port 2
Port 1: 192.168.20.2
Port 1: 192.168.20.2
Port 3
Port 3
Port 1: 192.168.20.3
Port 1: 192.168.20.3
Port 4
Port 4
Port 1: 192.168.20.4
Port 1: 192.168.20.4
ST-PUB-FW
ST-PUB-FW
DMZ Port: 192.168.100.1
DMZ Port: 192.168.100.1
Port 1: 192.168.100.2
Port 1: 192.168.100.2
Internal Port: 192.168.150.1
Internal Port: 192.168.150.1
Port 1: 192.168.150.2 
Port 1: 192.168.150.2 
ST_HO-PRV-
SW-AS
ST_HO-PRV-...
ST_DATA-PRV-SRV-DB
ST_DATA-PRV-SRV-DB
7
7
9
9
3
3
6
6
Port 1: 192.168.220.3
Port 1: 192.168.220.3
Port 3
Port 3
Port 1: 192.168.220.2
Port 1: 192.168.220.2
Port 2
Port 2


ST_PROJ_B-
PRV-PC-2
ST_PROJ_B-...
3
3
2
2
1
1
9
9


ST_PROJ_B-
PRV-SW-AS
ST_PROJ_B-...
Port 1: 192.168.240.3
Port 1: 192.168.240.3
Port 3
Port 3

SOME_TECH DMZ (ST_DMZ)
SOME_TECH DMZ (ST_DMZ)
ST_DMZ-PUB-SRV-WEB
ST_DMZ-PUB-SRV-WEB
ST_DMZ-PUB-SRV-WEB
ST_DMZ-PUB-SRV-WEB
9
9
3
3
5
5
Port 1
Port 1
Port 2: 192.168.200.1
Port 2: 192.168.200.1
Port 1
Port 1
Port 3: 192.168.210.1
Port 3: 192.168.210.1
ST_HO-PRV-PC-1
ST_HO-PRV-PC-1
3
3
2
2
1
1
9
9
Port 2
Port 2
Port 1: 192.168.200.2
Port 1: 192.168.200.2
ST_HO-PRV-
SW-AS
ST_HO-PRV-...
ST_HO-PRV-PC-3
ST_HO-PRV-PC-3
3
3
2
2
1
1
9
9
Port 4
Port 4
Port 1: 192.168.200.4
Port 1: 192.168.200.4
SOME_TECH HEAD OFFICE
(ST_HO)
SOME_TECH HEAD OFFICE...
ST_HO-PRV-PC-2
ST_HO-PRV-PC-2
3
3
2
2
1
1
9
9
Port 3
Port 3
Port 1: 192.168.200.3
Port 1: 192.168.200.3
Port 4: 192.168.220.1
Port 4: 192.168.220.1
Port 1
Port 1
Port 1
Port 1
Port 4: 192.168.250.1
Port 4: 192.168.250.1
Port 2: 192.168.230.1
Port 2: 192.168.230.1
Port 1
Port 1
Port 1
Port 1
Port 3: 192.168.240.1 
Port 3: 192.168.240.1 
ST_PROJ_A-
PRV-SW-AS
ST_PROJ_A-...
ST_PROJ_A-
PRV-PC-2
ST_PROJ_A-...
3
3
2
2
1
1
9
9
ST_PROJ_A-
PRV-PC-3
ST_PROJ_A-...
3
3
2
2
1
1
9
9
Port 1: 192.168.230.2
Port 1: 192.168.230.2
Port 2
Port 2
Port 1: 192.168.230.3
Port 1: 192.168.230.3
Port 3
Port 3
Port 1: 192.168.230.4
Port 1: 192.168.230.4
Port 4
Port 4
SOME TECH PROJECT B
(ST_PROJ_ B)
SOME TECH PROJECT B...
ST_PROJ_B-
PRV-PC-1
ST_PROJ_B-...
3
3
2
2
1
1
9
9
Port 1: 192.168.240.2
Port 1: 192.168.240.2
Port 2
Port 2
Port 1: 192.168.240.4
Port 1: 192.168.240.4
Port 4
Port 4
ST_PROJ_B-
PRV-PC-3
ST_PROJ_B-...
3
3
2
2
1
1
9
9
ST_HO-PRV-PC-3
ST_HO-PRV-PC-3
3
3
2
2
1
1
9
9
SOME_TECH Main Site Network
SOME_TECH Main Site Network
SOME TECH DATA (ST_DATA)
SOME TECH DATA (ST_DATA)



HOME OFFICE (HOME)
HOME OFFICE (HOME)


HOME-PUB-PC-1
HOME-PUB-PC-1
3
3
2
2
1
1
9
9
192.168.1.0/26
192.168.1.0/26
HOME-PUB-PC-2
HOME-PUB-PC-2
3
3
2
2
1
1
9
9
HOME-PUB-SRV
HOME-PUB-SRV
3
3
9
9
HOME-PUB-
SW-AS
HOME-PUB-...
Port 1: 192.168.1.2/26
Port 1: 192.168.1.2/26
Port 2
Port 2
Port 1: 192.168.1.3/26
Port 1: 192.168.1.3/26
Port 3
Port 3
Port 1
Port 1
Port 1: 192.168.1.1
Port 1: 192.168.1.1
HOME-PUB-
RT-DR
HOME-PUB-...
ISP-PUB-RT-BR
ISP-PUB-RT-BR


ISP-PUB-
SRV-DNS
ISP-PUB-...
8.8.8.0/28
8.8.8.0/28
10.1.10.0/30
10.1.10.0/30
INTERNET (ISP)
INTERNET (ISP)
REM-PUB-FW
REM-PUB-FW
Port 1: 192.168.1.4/26
Port 1: 192.168.1.4/26
Port 4
Port 4
REM-PUB-RT-DR
REM-PUB-RT-DR


Port 3: 10.1.10.1
Port 3: 10.1.10.1
External Port: 10.1.10.1
External Port: 10.1.10.1
Port 2: 10.1.0.2
Port 2: 10.1.0.2
Port 1: 10.1.0.1
Port 1: 10.1.0.1
Internal Port: 192.168.10.1
Internal Port: 192.168.10.1
Port 1: 192.168.10.2
Port 1: 192.168.10.2
Port 1: 8.8.8.8
Port 1: 8.8.8.8
Port 2: 8.8.8.1
Port 2: 8.8.8.1
4
4
10
10


Remote Site (REMOTE)
Remote Site (REMOTE)
REM-PUB-PC-2
REM-PUB-PC-2
3
3
2
2
1
1
9
9
REM-PUB-PC-1
REM-PUB-PC-1
3
3
2
2
1
1
9
9
Port 2
Port 2
Port 1: 192.168.20.2
Port 1: 192.168.20.2
Port 3
Port 3
Port 1: 192.168.20.3
Port 1: 192.168.20.3
Port 3
Port 3
Port 2: 192.168.20.1
Port 2: 192.168.20.1
Port 1
Port 1
REM-PUB-
SW-AS
REM-PUB-...
REM-PUB-SRV
REM-PUB-SRV
3
3
9
9
Port 1: 192.168.20.4
Port 1: 192.168.20.4
Port 4
Port 4
192.168.10.0/30
192.168.10.0/30
192.168.10.0/30
192.168.10.0/30
192.168.250.0/29
192.168.250.0/29
ST_DATA-PRV-SW-AS
ST_DATA-PRV-SW-AS
ST-DATA-PRV-SRV-STORAGE
ST-DATA-PRV-SRV-STOR...
3
3
8
8
9
9
192.168.220.0/29
192.168.220.0/29
192.168.200.0/29
192.168.200.0/29
192.168.50.0/28
192.168.50.0/28
10.1.100.0/30
10.1.100.0/30
192.168.100.0/30
192.168.100.0/30
10.1.0.0/30
10.1.0.0/30
Node Software
Number Key
Node Software...
SERVICES
SERVICES
APPLICATIONS
APPLICATIONS
1
1
2
2
DatabaseClient
DatabaseClient
Webbrowser
Webbrowser
3
3
DNSClient
DNSClient
4
4
Web Server
Web Server
5
5
WebServer
WebServer
6
6
DatabaseService
DatabaseService
7
7
FTP Client
FTP Client
8
8
FTP Server
FTP Server
9
9
NTP Client
NTP Client
10
10
NTP Server
NTP Server

UC7 Detailed Network Diagram

UC7 Detailed Network Diagram
Node Type Colour Key
Node Type Colour Key
ROUTER
#CFE4FF
ROUTER...
SERVER
#CED4DB
SERVER...
FIREWALL
#FFD9D9
FIREWALL...
SWITCH
#FFF7A1
SWITCH...
COMPUTER
#E9E9E9
COMPUTER...
Text is not SVG - cannot display
diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_activate.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_activate.png new file mode 100644 index 00000000..b8f283e4 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_activate.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_c2.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_c2.png new file mode 100644 index 00000000..4a3f90db Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_c2.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_download_install.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_download_install.png new file mode 100644 index 00000000..4d7a5f20 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_download_install.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_main.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_main.png new file mode 100644 index 00000000..4d86d0a7 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_main.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_payload.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_payload.png new file mode 100644 index 00000000..ffd89300 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_payload.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_1.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_1.png new file mode 100644 index 00000000..1b6b6a1d Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_1.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_2.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_2.png new file mode 100644 index 00000000..4faa8eb4 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_2.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_3.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_3.png new file mode 100644 index 00000000..1f00d3a8 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_3.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_4.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_4.png new file mode 100644 index 00000000..01cddf5c Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_4.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001_training_static_red.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001_training_static_red.png new file mode 100644 index 00000000..9bb6ca68 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001_training_static_red.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap003/uc7_tap003_main.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap003/uc7_tap003_main.png new file mode 100644 index 00000000..2882fd3b Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap003/uc7_tap003_main.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap003_training_static_red.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap003_training_static_red.png new file mode 100644 index 00000000..428d60e8 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap003_training_static_red.png differ diff --git a/src/primaite/session/environment.py b/src/primaite/session/environment.py index b7a9a042..fa545dbc 100644 --- a/src/primaite/session/environment.py +++ b/src/primaite/session/environment.py @@ -26,14 +26,26 @@ except ModuleNotFoundError: _LOGGER.debug("Torch not available for importing") -def set_random_seed(seed: int) -> Union[None, int]: +def set_random_seed(seed: int, generate_seed_value: bool) -> Union[None, int]: """ Set random number generators. + If seed is None or -1 and generate_seed_value is True randomly generate a + seed value. + If seed is > -1 and generate_seed_value is True ignore the latter and use + the provide seed value. + :param seed: int + :param generate_seed_value: bool + :return: None or the int representing the seed used. """ if seed is None or seed == -1: - return None + if generate_seed_value: + rng = np.random.default_rng() + # 2**32-1 is highest value for python RNG seed. + seed = int(rng.integers(low=0, high=2**32 - 1)) + else: + return None elif seed < -1: raise ValueError("Invalid random number seed") # Seed python RNG @@ -50,6 +62,13 @@ def set_random_seed(seed: int) -> Union[None, int]: return seed +def log_seed_value(seed: int): + """Log the selected seed value to file.""" + path = SIM_OUTPUT.path / "seed.log" + with open(path, "w") as file: + file.write(f"Seed value = {seed}") + + class PrimaiteGymEnv(gymnasium.Env): """ Thin wrapper env to provide agents with a gymnasium API. @@ -65,7 +84,8 @@ class PrimaiteGymEnv(gymnasium.Env): """Object that returns a config corresponding to the current episode.""" self.seed = self.episode_scheduler(0).get("game", {}).get("seed") """Get RNG seed from config file. NB: Must be before game instantiation.""" - self.seed = set_random_seed(self.seed) + self.generate_seed_value = self.episode_scheduler(0).get("game", {}).get("generate_seed_value") + self.seed = set_random_seed(self.seed, self.generate_seed_value) self.io = PrimaiteIO.from_config(self.episode_scheduler(0).get("io_settings", {})) """Handles IO for the environment. This produces sys logs, agent logs, etc.""" self.game: PrimaiteGame = PrimaiteGame.from_config(self.episode_scheduler(0)) @@ -79,6 +99,8 @@ class PrimaiteGymEnv(gymnasium.Env): _LOGGER.info(f"PrimaiteGymEnv RNG seed = {self.seed}") + log_seed_value(self.seed) + def action_masks(self) -> np.ndarray: """ Return the action mask for the agent. @@ -146,7 +168,7 @@ class PrimaiteGymEnv(gymnasium.Env): f"avg. reward: {self.agent.reward_function.total_reward}" ) if seed is not None: - set_random_seed(seed) + set_random_seed(seed, self.generate_seed_value) self.total_reward_per_episode[self.episode_counter] = self.agent.reward_function.total_reward if self.io.settings.save_agent_actions: diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 8653359a..50a7d2a4 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -864,7 +864,21 @@ class UserManager(Service, discriminator="user-manager"): """ rm = super()._init_request_manager() - # todo add doc about requeest schemas + # todo add doc about request schemas + rm.add_request( + "add_user", + RequestType( + func=lambda request, context: RequestResponse.from_bool( + self.add_user(username=request[0], password=request[1], is_admin=request[2]) + ) + ), + ) + rm.add_request( + "disable_user", + RequestType( + func=lambda request, context: RequestResponse.from_bool(self.disable_user(username=request[0])) + ), + ) rm.add_request( "change_password", RequestType( @@ -1572,7 +1586,7 @@ class Node(SimComponent, ABC): operating_state: Any = None - users: Any = None # Temporary to appease "extra=forbid" + users: List[Dict] = [] # Temporary to appease "extra=forbid" config: ConfigSchema = Field(default_factory=lambda: Node.ConfigSchema()) """Configuration items within Node""" @@ -1638,6 +1652,8 @@ class Node(SimComponent, ABC): self._install_system_software() self.session_manager.node = self self.session_manager.software_manager = self.software_manager + for user in self.config.users: + self.user_manager.add_user(**user, bypass_can_perform_action=True) @property def user_manager(self) -> Optional[UserManager]: @@ -1769,7 +1785,7 @@ class Node(SimComponent, ABC): """ application_name = request[0] if self.software_manager.software.get(application_name): - self.sys_log.warning(f"Can't install {application_name}. It's already installed.") + self.sys_log.info(f"Can't install {application_name}. It's already installed.") return RequestResponse(status="success", data={"reason": "already installed"}) application_class = Application._registry[application_name] self.software_manager.install(application_class) diff --git a/src/primaite/simulator/network/hardware/nodes/host/host_node.py b/src/primaite/simulator/network/hardware/nodes/host/host_node.py index 76d9167c..86ac790c 100644 --- a/src/primaite/simulator/network/hardware/nodes/host/host_node.py +++ b/src/primaite/simulator/network/hardware/nodes/host/host_node.py @@ -2,11 +2,12 @@ from __future__ import annotations from ipaddress import IPv4Address -from typing import Any, ClassVar, Dict, Literal, Optional +from typing import Any, ClassVar, Dict, List, Literal, Optional from pydantic import Field from primaite import getLogger +from primaite.simulator.file_system.file_type import FileType from primaite.simulator.network.hardware.base import ( IPWiredNetworkInterface, Link, @@ -313,7 +314,7 @@ class HostNode(Node, discriminator="host-node"): """ SYSTEM_SOFTWARE: ClassVar[Dict] = { - "HostARP": HostARP, + "host-arp": HostARP, "icmp": ICMP, "dns-client": DNSClient, "ntp-client": NTPClient, @@ -339,7 +340,7 @@ class HostNode(Node, discriminator="host-node"): ip_address: IPV4Address services: Any = None # temporarily unset to appease extra="forbid" applications: Any = None # temporarily unset to appease extra="forbid" - folders: Any = None # temporarily unset to appease extra="forbid" + folders: List[Dict] = {} # temporarily unset to appease extra="forbid" network_interfaces: Any = None # temporarily unset to appease extra="forbid" config: ConfigSchema = Field(default_factory=lambda: HostNode.ConfigSchema()) @@ -348,6 +349,18 @@ class HostNode(Node, discriminator="host-node"): super().__init__(**kwargs) self.connect_nic(NIC(ip_address=kwargs["config"].ip_address, subnet_mask=kwargs["config"].subnet_mask)) + for folder in self.config.folders: + # handle empty foler defined by just a string + self.file_system.create_folder(folder["folder_name"]) + + for file in folder.get("files", []): + self.file_system.create_file( + folder_name=folder["folder_name"], + file_name=file["file_name"], + size=file.get("size", 0), + file_type=FileType[file.get("type", "UNKNOWN").upper()], + ) + @property def nmap(self) -> Optional[NMAP]: """ diff --git a/src/primaite/simulator/network/hardware/nodes/network/firewall.py b/src/primaite/simulator/network/hardware/nodes/network/firewall.py index 2cbc23d2..c872b8b3 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/firewall.py +++ b/src/primaite/simulator/network/hardware/nodes/network/firewall.py @@ -49,7 +49,7 @@ class Firewall(Router, discriminator="firewall"): Example: >>> from primaite.simulator.network.transmission.network_layer import IPProtocol - >>> from primaite.simulator.network.transmission.transport_layer import Port + >>> from primaite.utils.validation.port import Port >>> firewall = Firewall(hostname="Firewall1") >>> firewall.configure_internal_port(ip_address="192.168.1.1", subnet_mask="255.255.255.0") >>> firewall.configure_external_port(ip_address="10.0.0.1", subnet_mask="255.255.255.0") diff --git a/src/primaite/simulator/network/hardware/nodes/network/router.py b/src/primaite/simulator/network/hardware/nodes/network/router.py index 3b35600b..47ee3169 100644 --- a/src/primaite/simulator/network/hardware/nodes/network/router.py +++ b/src/primaite/simulator/network/hardware/nodes/network/router.py @@ -467,6 +467,7 @@ class AccessControlList(SimComponent): """Check if a packet with the given properties is permitted through the ACL.""" permitted = False rule: ACLRule = None + for _rule in self._acl: if not _rule: continue @@ -1215,9 +1216,9 @@ class Router(NetworkNode, discriminator="router"): config: ConfigSchema = Field(default_factory=lambda: Router.ConfigSchema()) SYSTEM_SOFTWARE: ClassVar[Dict] = { - "UserSessionManager": UserSessionManager, - "UserManager": UserManager, - "Terminal": Terminal, + "user-session-manager": UserSessionManager, + "user-manager": UserManager, + "terminal": Terminal, } network_interfaces: Dict[str, RouterInterface] = {} @@ -1385,6 +1386,12 @@ class Router(NetworkNode, discriminator="router"): return False + def subject_to_acl(self, frame: Frame) -> bool: + """Check that frame is subject to ACL rules.""" + if frame.ip.protocol == "udp" and frame.is_arp: + return False + return True + def receive_frame(self, frame: Frame, from_network_interface: RouterInterface): """ Processes an incoming frame received on one of the router's interfaces. @@ -1398,8 +1405,12 @@ class Router(NetworkNode, discriminator="router"): if self.operating_state != NodeOperatingState.ON: return - # Check if it's permitted - permitted, rule = self.acl.is_permitted(frame) + if self.subject_to_acl(frame=frame): + # Check if it's permitted + permitted, rule = self.acl.is_permitted(frame) + else: + permitted = True + rule = None if not permitted: at_port = self._get_port_of_nic(from_network_interface) diff --git a/src/primaite/simulator/network/transmission/data_link_layer.py b/src/primaite/simulator/network/transmission/data_link_layer.py index e7c2a124..a07194a4 100644 --- a/src/primaite/simulator/network/transmission/data_link_layer.py +++ b/src/primaite/simulator/network/transmission/data_link_layer.py @@ -163,7 +163,7 @@ class Frame(BaseModel): """ Checks if the Frame is an ARP (Address Resolution Protocol) packet. - This is determined by checking if the destination port of the TCP header is equal to the ARP port. + This is determined by checking if the destination and source port of the UDP header is equal to the ARP port. :return: True if the Frame is an ARP packet, otherwise False. """ diff --git a/src/primaite/simulator/system/services/arp/arp.py b/src/primaite/simulator/system/services/arp/arp.py index b0630d5d..c6b687ce 100644 --- a/src/primaite/simulator/system/services/arp/arp.py +++ b/src/primaite/simulator/system/services/arp/arp.py @@ -55,7 +55,7 @@ class ARP(Service, discriminator="arp"): :param markdown: If True, format the output as Markdown. Otherwise, use plain text. """ - table = PrettyTable(["IP Address", "MAC Address", "Via"]) + table = PrettyTable(["IP Address", "MAC Address", "Via", "Port"]) if markdown: table.set_style(MARKDOWN) table.align = "l" @@ -66,6 +66,7 @@ class ARP(Service, discriminator="arp"): str(ip), arp.mac_address, self.software_manager.node.network_interfaces[arp.network_interface_uuid].mac_address, + self.software_manager.node.network_interfaces[arp.network_interface_uuid].port_num, ] ) print(table) diff --git a/src/primaite/simulator/system/services/terminal/terminal.py b/src/primaite/simulator/system/services/terminal/terminal.py index 2ce7d176..112f6abc 100644 --- a/src/primaite/simulator/system/services/terminal/terminal.py +++ b/src/primaite/simulator/system/services/terminal/terminal.py @@ -142,12 +142,20 @@ class Terminal(Service, discriminator="terminal"): _client_connection_requests: Dict[str, Optional[Union[str, TerminalClientConnection]]] = {} """Dictionary of connect requests made to remote nodes.""" + _last_response: Optional[RequestResponse] = None + """Last response received from RequestManager, for returning remote RequestResponse.""" + def __init__(self, **kwargs): kwargs["name"] = "terminal" kwargs["port"] = PORT_LOOKUP["SSH"] kwargs["protocol"] = PROTOCOL_LOOKUP["TCP"] super().__init__(**kwargs) + @property + def last_response(self) -> Optional[RequestResponse]: + """Public version of _last_response attribute.""" + return self._last_response + def describe_state(self) -> Dict: """ Produce a dictionary describing the current state of this object. @@ -186,7 +194,7 @@ class Terminal(Service, discriminator="terminal"): return RequestResponse(status="failure", data={}) rm.add_request( - "node-session-remote-login", + "node_session_remote_login", request_type=RequestType(func=_remote_login), ) @@ -209,28 +217,45 @@ class Terminal(Service, discriminator="terminal"): command: str = request[1]["command"] remote_connection = self._get_connection_from_ip(ip_address=ip_address) if remote_connection: - outcome = remote_connection.execute(command) - if outcome: - return RequestResponse( - status="success", - data={}, - ) - else: - return RequestResponse( - status="failure", - data={}, - ) + remote_connection.execute(command) + return self.last_response if not None else RequestResponse(status="failure", data={}) + return RequestResponse( + status="failure", + data={"reason": "Failed to execute command."}, + ) rm.add_request( "send_remote_command", request_type=RequestType(func=remote_execute_request), ) + def local_execute_request(request: RequestFormat, context: Dict) -> RequestResponse: + """Executes a command using a local terminal session.""" + command: str = request[2]["command"] + local_connection = self._process_local_login(username=request[0], password=request[1]) + if local_connection: + outcome = local_connection.execute(command) + if outcome: + return RequestResponse( + status="success", + data={"reason": outcome}, + ) + return RequestResponse( + status="success", + data={"reason": "Local Terminal failed to resolve command. Potentially invalid credentials?"}, + ) + + rm.add_request( + "send_local_command", + request_type=RequestType(func=local_execute_request), + ) + return rm def execute(self, command: List[Any]) -> Optional[RequestResponse]: """Execute a passed ssh command via the request manager.""" - return self.parent.apply_request(command) + self._last_response = self.parent.apply_request(command) + return self._last_response def _get_connection_from_ip(self, ip_address: IPv4Address) -> Optional[RemoteTerminalConnection]: """Find Remote Terminal Connection from a given IP.""" @@ -409,6 +434,8 @@ class Terminal(Service, discriminator="terminal"): """ source_ip = kwargs["frame"].ip.src_ip_address self.sys_log.info(f"{self.name}: Received payload: {payload}. Source: {source_ip}") + self._last_response = None # Clear last response + if isinstance(payload, SSHPacket): if payload.transport_message == SSHTransportMessage.SSH_MSG_USERAUTH_REQUEST: # validate & add connection @@ -457,6 +484,9 @@ class Terminal(Service, discriminator="terminal"): session_id=session_id, source_ip=source_ip, ) + self._last_response: RequestResponse = RequestResponse( + status="success", data={"reason": "Login Successful"} + ) elif payload.transport_message == SSHTransportMessage.SSH_MSG_SERVICE_REQUEST: # Requesting a command to be executed @@ -468,12 +498,32 @@ class Terminal(Service, discriminator="terminal"): payload.connection_uuid ) remote_session.last_active_step = self.software_manager.node.user_session_manager.current_timestep - self.execute(command) + self._last_response: RequestResponse = self.execute(command) + + if self._last_response.status == "success": + transport_message = SSHTransportMessage.SSH_MSG_SERVICE_SUCCESS + else: + transport_message = SSHTransportMessage.SSH_MSG_SERVICE_FAILED + + payload: SSHPacket = SSHPacket( + payload=self._last_response, + transport_message=transport_message, + connection_message=SSHConnectionMessage.SSH_MSG_CHANNEL_DATA, + ) + self.software_manager.send_payload_to_session_manager( + payload=payload, dest_port=self.port, session_id=session_id + ) return True else: self.sys_log.error( f"{self.name}: Connection UUID:{payload.connection_uuid} is not valid. Rejecting Command." ) + elif ( + payload.transport_message == SSHTransportMessage.SSH_MSG_SERVICE_SUCCESS + or SSHTransportMessage.SSH_MSG_SERVICE_FAILED + ): + # Likely receiving command ack from remote. + self._last_response = payload.payload if isinstance(payload, dict) and payload.get("type"): if payload["type"] == "disconnect": diff --git a/src/primaite/simulator/system/services/web_server/web_server.py b/src/primaite/simulator/system/services/web_server/web_server.py index 2eddefc1..3f8760c4 100644 --- a/src/primaite/simulator/system/services/web_server/web_server.py +++ b/src/primaite/simulator/system/services/web_server/web_server.py @@ -117,37 +117,44 @@ class WebServer(Service, discriminator="web-server"): :type: payload: HttpRequestPacket """ response = HttpResponsePacket(status_code=HttpStatusCode.NOT_FOUND, payload=payload) - try: - parsed_url = urlparse(payload.request_url) - path = parsed_url.path.strip("/") - if len(path) < 1: + parsed_url = urlparse(payload.request_url) + path = parsed_url.path.strip("/") if parsed_url and parsed_url.path else "" + + if len(path) < 1: + # query succeeded + response.status_code = HttpStatusCode.OK + + if path.startswith("users"): + # get data from DatabaseServer + # get all users + if not self._establish_db_connection(): + # unable to create a db connection + response.status_code = HttpStatusCode.INTERNAL_SERVER_ERROR + return response + + if self.db_connection.query("SELECT"): # query succeeded + self.set_health_state(SoftwareHealthState.GOOD) response.status_code = HttpStatusCode.OK + else: + self.set_health_state(SoftwareHealthState.COMPROMISED) + return response - if path.startswith("users"): - # get data from DatabaseServer - # get all users - if not self.db_connection: - self._establish_db_connection() - - if self.db_connection.query("SELECT"): - # query succeeded - self.set_health_state(SoftwareHealthState.GOOD) - response.status_code = HttpStatusCode.OK - else: - self.set_health_state(SoftwareHealthState.COMPROMISED) - - return response - except Exception: # TODO: refactor this. Likely to cause silent bugs. (ADO ticket #2345 ) - # something went wrong on the server - response.status_code = HttpStatusCode.INTERNAL_SERVER_ERROR - return response - - def _establish_db_connection(self) -> None: + def _establish_db_connection(self) -> bool: """Establish a connection to db.""" + # if active db connection, return true + if self.db_connection: + return True + + # otherwise, try to create db connection db_client = self.software_manager.software.get("database-client") + + if db_client is None: + return False # database client not installed + self.db_connection: DatabaseClientConnection = db_client.get_new_connection() + return self.db_connection is not None def send( self, diff --git a/tests/assets/configs/basic_switched_network.yaml b/tests/assets/configs/basic_switched_network.yaml index c9ac5f8d..7ffe4c08 100644 --- a/tests/assets/configs/basic_switched_network.yaml +++ b/tests/assets/configs/basic_switched_network.yaml @@ -25,7 +25,19 @@ game: - ICMP - TCP - UDP - + thresholds: + nmne: + high: 100 + medium: 25 + low: 5 + file_access: + high: 10 + medium: 5 + low: 2 + app_executions: + high: 5 + medium: 3 + low: 2 agents: - ref: client_2_green_user team: GREEN @@ -64,10 +76,16 @@ agents: options: hosts: - hostname: client_1 + applications: + - application_name: WebBrowser + folders: + - folder_name: root + files: + - file_name: "test.txt" - hostname: client_2 - hostname: client_3 num_services: 1 - num_applications: 0 + num_applications: 1 num_folders: 1 num_files: 1 num_nics: 2 @@ -182,6 +200,10 @@ simulation: options: ntp_server_ip: 192.168.1.10 - type: ntp-server + folders: + - folder_name: root + files: + - file_name: test.txt - hostname: client_2 type: computer ip_address: 192.168.10.22 diff --git a/tests/assets/configs/nodes_with_initial_files.yaml b/tests/assets/configs/nodes_with_initial_files.yaml new file mode 100644 index 00000000..d4c6406b --- /dev/null +++ b/tests/assets/configs/nodes_with_initial_files.yaml @@ -0,0 +1,226 @@ +# Basic Switched network +# +# -------------- -------------- -------------- +# | client_1 |------| switch_1 |------| client_2 | +# -------------- -------------- -------------- +# +io_settings: + save_step_metadata: false + save_pcap_logs: true + save_sys_logs: true + sys_log_level: WARNING + agent_log_level: INFO + save_agent_logs: true + write_agent_log_to_terminal: True + + +game: + max_episode_length: 256 + ports: + - ARP + - DNS + - HTTP + - POSTGRES_SERVER + protocols: + - ICMP + - TCP + - UDP + +agents: + - ref: client_2_green_user + team: GREEN + type: periodic-agent + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_id: 0 + application_id: 0 + + agent_settings: + possible_start_nodes: [client_2,] + target_application: web-browser + start_step: 5 + frequency: 4 + variance: 3 + + + + - ref: defender + team: BLUE + type: proxy-agent + + observation_space: + type: custom + options: + components: + - type: nodes + label: NODES + options: + hosts: + - hostname: client_1 + - hostname: client_2 + - hostname: client_3 + num_services: 1 + num_applications: 0 + num_folders: 1 + num_files: 1 + num_nics: 2 + include_num_access: false + monitored_traffic: + icmp: + - NONE + tcp: + - DNS + include_nmne: false + routers: + - hostname: router_1 + num_ports: 0 + ip_list: + - 192.168.10.21 + - 192.168.10.22 + - 192.168.10.23 + wildcard_list: + - 0.0.0.1 + port_list: + - 80 + - 5432 + protocol_list: + - ICMP + - TCP + - UDP + num_rules: 10 + + - type: links + label: LINKS + options: + link_references: + - switch_1:eth-1<->client_1:eth-1 + - switch_1:eth-2<->client_2:eth-1 + - type: none + label: ICS + options: {} + + action_space: + action_map: + 0: + action: do-nothing + options: {} + + reward_function: + reward_components: + - type: database-file-integrity + weight: 0.5 + options: + node_hostname: database_server + folder_name: database + file_name: database.db + + - type: web-server-404-penalty + weight: 0.5 + options: + node_hostname: web_server + service_name: web_server_web_service + + + agent_settings: + flatten_obs: true + +simulation: + network: + nodes: + + - type: switch + hostname: switch_1 + num_ports: 8 + + - hostname: client_1 + type: computer + ip_address: 192.168.10.21 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.10.1 + dns_server: 192.168.1.10 + applications: + - type: ransomware-script + - type: web-browser + options: + target_url: http://arcd.com/users/ + - type: database-client + options: + db_server_ip: 192.168.1.10 + server_password: arcd + - type: data-manipulation-bot + options: + port_scan_p_of_success: 0.8 + data_manipulation_p_of_success: 0.8 + payload: "DELETE" + server_ip: 192.168.1.21 + server_password: arcd + - type: dos-bot + options: + target_ip_address: 192.168.10.21 + payload: SPOOF DATA + port_scan_p_of_success: 0.8 + services: + - type: dns-client + options: + dns_server: 192.168.1.10 + - type: dns-server + options: + domain_mapping: + arcd.com: 192.168.1.10 + - type: database-service + options: + backup_server_ip: 192.168.1.10 + - type: web-server + - type: ftp-server + options: + server_password: arcd + - type: ntp-client + options: + ntp_server_ip: 192.168.1.10 + - type: ntp-server + - hostname: client_2 + type: computer + ip_address: 192.168.10.22 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.10.1 + dns_server: 192.168.1.10 + folders: + - folder_name: empty_folder + - folder_name: downloads + files: + - file_name: "test.txt" + - file_name: "another_file.pwtwoti" + - folder_name: root + files: + - file_name: passwords + size: 663 + type: TXT + # pre installed services and applications + - hostname: client_3 + type: computer + ip_address: 192.168.10.23 + subnet_mask: 255.255.255.0 + default_gateway: 192.168.10.1 + dns_server: 192.168.1.10 + start_up_duration: 0 + shut_down_duration: 0 + operating_state: "OFF" + # pre installed services and applications + + links: + - endpoint_a_hostname: switch_1 + endpoint_a_port: 1 + endpoint_b_hostname: client_1 + endpoint_b_port: 1 + bandwidth: 200 + - endpoint_a_hostname: switch_1 + endpoint_a_port: 2 + endpoint_b_hostname: client_2 + endpoint_b_port: 1 + bandwidth: 200 diff --git a/tests/e2e_integration_tests/test_uc7_agents.py b/tests/e2e_integration_tests/test_uc7_agents.py new file mode 100644 index 00000000..8c597dc7 --- /dev/null +++ b/tests/e2e_integration_tests/test_uc7_agents.py @@ -0,0 +1,173 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK + +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG, load +from primaite.game.game import PrimaiteGame +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.network.container import Network +from primaite.simulator.network.hardware.nodes.host.computer import Computer +from primaite.simulator.network.hardware.nodes.host.server import Server +from primaite.simulator.network.hardware.nodes.network.firewall import Firewall +from primaite.simulator.system.applications.application import ApplicationOperatingState +from primaite.simulator.system.applications.database_client import DatabaseClient +from primaite.simulator.system.applications.red_applications.c2.c2_beacon import C2Beacon +from primaite.simulator.system.applications.red_applications.ransomware_script import RansomwareScript +from primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.system.services.database.database_service import DatabaseService +from primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.system.services.dns.dns_server import DNSServer +from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.ftp.ftp_server import FTPServer +from primaite.simulator.system.services.ntp.ntp_client import NTPClient +from primaite.simulator.system.services.ntp.ntp_server import NTPServer +from primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.system.software import SoftwareHealthState + +CONFIG_FILE = _EXAMPLE_CFG / "uc7_config.yaml" + + +@pytest.fixture(scope="function") +def uc7_environment() -> PrimaiteGymEnv: + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def assert_agent_reward(env: PrimaiteGymEnv, agent_name: str, positive: bool): + """Asserts that a given agent has a reward that is below/above or equal to 0 dependant on arguments.""" + agent_reward = env.game.agents[agent_name].reward_function.total_reward + if agent_name == "defender": + return # ignore blue agent + if positive is True: + assert agent_reward >= 0 # Asserts that no agents are below a total reward of 0 + elif positive is False: + assert agent_reward <= 0 # Asserts that no agents are above a total reward of 0 + else: + print("Invalid 'positive' argument.") + + +def test_green_agent_positive_reward(uc7_environment): + """Confirms that the UC7 Green Agents receive a positive reward (Default Behaviour).""" + env: PrimaiteGymEnv = uc7_environment + + # Performing no changes to the environment. Default Behaviour + + # Stepping 60 times in the environment + for _ in range(60): + env.step(0) + + for agent in env.game.agents: + assert_agent_reward(env=env, agent_name=env.game.agents[agent].config.ref, positive=True) + + +def test_green_agent_negative_reward(uc7_environment): + """Confirms that the UC7 Green Agents receive a negative reward. (Disabled web-server and database-service)""" + + env: PrimaiteGymEnv = uc7_environment + + # Purposefully disabling the following services: + + # 1. Disabling the web-server + st_dmz_pub_srv_web: Server = env.game.simulation.network.get_node_by_hostname("ST-DMZ-PUB-SRV-WEB") + st_web_server = st_dmz_pub_srv_web.software_manager.software["web-server"] + st_web_server.operating_state = ServiceOperatingState.DISABLED + assert st_web_server.operating_state == ServiceOperatingState.DISABLED + + # 2. Disabling the DatabaseServer + st_data_database_server: Server = env.game.simulation.network.get_node_by_hostname("ST-DATA-PRV-SRV-DB") + database_service: DatabaseService = st_data_database_server.software_manager.software["database-service"] + database_service.operating_state = ServiceOperatingState.DISABLED + assert database_service.operating_state == ServiceOperatingState.DISABLED + + # Stepping 100 times in the environment + for _ in range(100): + env.step(0) + + for agent in env.game.agents: + assert_agent_reward(env=env, agent_name=env.game.agents[agent].config.ref, positive=False) + + +def test_tap001_default_behaviour(uc7_environment): + """Confirms that the TAP001 expected simulation impacts works as expected in the UC7 environment.""" + env: PrimaiteGymEnv = uc7_environment + env.reset() + network = env.game.simulation.network + + # Running for 128 episodes + for _ in range(128): + env.step(0) + + some_tech_proj_a_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-A-PRV-PC-1") + + # Asserting that the `malware_dropper.ps1` was created. + + malware_dropper_file: File = some_tech_proj_a_pc_1.file_system.get_file("downloads", "malware_dropper.ps1") + assert malware_dropper_file.health_status == FileSystemItemHealthStatus.GOOD + + # Asserting that the `RansomwareScript` launched successfully. + + ransomware_script: RansomwareScript = some_tech_proj_a_pc_1.software_manager.software["ransomware-script"] + assert ransomware_script.health_state_actual == SoftwareHealthState.GOOD + assert ransomware_script.operating_state == ApplicationOperatingState.RUNNING + + # Asserting that the `C2Beacon` connected to the `C2Server`. + + c2_beacon: C2Beacon = some_tech_proj_a_pc_1.software_manager.software["c2-beacon"] + assert c2_beacon.health_state_actual == SoftwareHealthState.GOOD + assert c2_beacon.operating_state == ApplicationOperatingState.RUNNING + assert c2_beacon.c2_connection_active == True + + # Asserting that the target database was successfully corrupted. + some_tech_data_server_database: Server = network.get_node_by_hostname("ST-DATA-PRV-SRV-DB") + database_file: File = some_tech_data_server_database.file_system.get_file( + folder_name="database", file_name="database.db" + ) + assert database_file.health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_tap003_default_behaviour(uc7_environment): + """Confirms that the TAP003 expected simulation impacts works as expected in the UC7 environment.""" + from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router + from primaite.simulator.network.transmission.network_layer import IPPacket, IPProtocol + from primaite.utils.validation.port import PORT_LOOKUP + + def uc7_environment_tap003() -> PrimaiteGymEnv: + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["agents"][32]["agent_settings"]["starting_nodes"] = ["ST-PROJ-A-PRV-PC-1"] + cfg["agents"][32]["agent_settings"]["default_starting_node"] = "ST-PROJ-A-PRV-PC-1" + env = PrimaiteGymEnv(env_config=cfg) + return env + + env: PrimaiteGymEnv = uc7_environment_tap003() + env.reset() + # Running for 128 episodes + for _ in range(128): + env.step(0) + network = env.game.simulation.network + + # Asserting that a malicious ACL has been added to ST-INTRA-PRV-RT-DR-1 + st_intra_prv_rt_dr_1: Router = network.get_node_by_hostname(hostname="ST-INTRA-PRV-RT-DR-1") + assert st_intra_prv_rt_dr_1.acl.acl[1].action == ACLAction.DENY + assert st_intra_prv_rt_dr_1.acl.acl[1].protocol == "tcp" + assert st_intra_prv_rt_dr_1.acl.acl[1].src_port == PORT_LOOKUP.get("POSTGRES_SERVER") + assert st_intra_prv_rt_dr_1.acl.acl[1].dst_port == PORT_LOOKUP.get("POSTGRES_SERVER") + + # Asserting that a malicious ACL has been added to ST-INTRA-PRV-RT-CR + st_intra_prv_rt_cr: Router = network.get_node_by_hostname(hostname="ST-INTRA-PRV-RT-CR") + assert st_intra_prv_rt_cr.acl.acl[1].action == ACLAction.DENY + assert st_intra_prv_rt_cr.acl.acl[1].protocol == "tcp" + assert st_intra_prv_rt_cr.acl.acl[1].src_port == PORT_LOOKUP.get("HTTP") + assert st_intra_prv_rt_cr.acl.acl[1].dst_port == PORT_LOOKUP.get("HTTP") + + # Asserting that a malicious ACL has been added to REM-PUB-RT-DR + rem_pub_rt_dr: Router = network.get_node_by_hostname(hostname="REM-PUB-RT-DR") + assert rem_pub_rt_dr.acl.acl[1].action == ACLAction.DENY + assert rem_pub_rt_dr.acl.acl[1].protocol == "tcp" + assert rem_pub_rt_dr.acl.acl[1].src_port == PORT_LOOKUP.get("DNS") + assert rem_pub_rt_dr.acl.acl[1].dst_port == PORT_LOOKUP.get("DNS") diff --git a/tests/e2e_integration_tests/test_uc7_route_connectivity.py b/tests/e2e_integration_tests/test_uc7_route_connectivity.py new file mode 100644 index 00000000..25350c4b --- /dev/null +++ b/tests/e2e_integration_tests/test_uc7_route_connectivity.py @@ -0,0 +1,237 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.game import PrimaiteGame +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.network.container import Network +from primaite.simulator.network.hardware.nodes.host.computer import Computer +from primaite.simulator.network.hardware.nodes.host.server import Server +from primaite.simulator.network.hardware.nodes.network.firewall import Firewall +from primaite.simulator.network.hardware.nodes.network.router import Router +from primaite.simulator.network.hardware.nodes.network.switch import Switch + +CONFIG_FILE = _EXAMPLE_CFG / "uc7_config.yaml" + + +@pytest.fixture(scope="function") +def uc7_network() -> Network: + with open(file=CONFIG_FILE, mode="r") as f: + cfg = yaml.safe_load(stream=f) + + game = PrimaiteGame.from_config(cfg=cfg) + return game.simulation.network + + +def test_ping_home_office(uc7_network): + """Asserts that all home_pub_* can ping each-other and the public dns (isp_pub_srv_dns)""" + network = uc7_network + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + home_pub_pc_2: Computer = network.get_node_by_hostname("HOME-PUB-PC-2") + home_pub_pc_srv: Server = network.get_node_by_hostname("HOME-PUB-SRV") + home_pub_rt_dr: Router = network.get_node_by_hostname("HOME-PUB-RT-DR") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + assert home_pub_pc_1.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + def ping_all_home_office(host): + assert host.ping(home_pub_pc_1.network_interface[1].ip_address) + assert host.ping(home_pub_pc_2.network_interface[1].ip_address) + assert host.ping(home_pub_pc_srv.network_interface[1].ip_address) + assert host.ping(home_pub_rt_dr.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_home_office(home_pub_pc_1) + ping_all_home_office(home_pub_pc_2) + ping_all_home_office(home_pub_pc_srv) + ping_all_home_office(isp_pub_srv_dns) + + +def test_ping_remote_site(uc7_network): + """Asserts that all remote_pub_* hosts can ping each-other and the public dns server (isp_pub_srv_dns)""" + network = uc7_network + rem_pub_fw: Firewall = network.get_node_by_hostname(hostname="REM-PUB-FW") + rem_pub_rt_dr: Router = network.get_node_by_hostname(hostname="REM-PUB-RT-DR") + rem_pub_pc_1: Computer = network.get_node_by_hostname(hostname="REM-PUB-PC-1") + rem_pub_pc_2: Computer = network.get_node_by_hostname(hostname="REM-PUB-PC-2") + rem_pub_srv: Computer = network.get_node_by_hostname(hostname="REM-PUB-SRV") + + def ping_all_remote_site(host): + assert host.ping(rem_pub_fw.network_interface[1].ip_address) + assert host.ping(rem_pub_rt_dr.network_interface[1].ip_address) + assert host.ping(rem_pub_pc_1.network_interface[1].ip_address) + assert host.ping(rem_pub_pc_2.network_interface[1].ip_address) + assert host.ping(rem_pub_srv.network_interface[1].ip_address) + + ping_all_remote_site(host=rem_pub_fw) + ping_all_remote_site(host=rem_pub_rt_dr) + ping_all_remote_site(host=rem_pub_pc_1) + ping_all_remote_site(host=rem_pub_pc_2) + ping_all_remote_site(host=rem_pub_srv) + + +def test_ping_some_tech_dmz(uc7_network): + """Asserts that the st_dmz_pub_srv_web and the st_public_firewall can ping each other and remote site and home office.""" + network = uc7_network + st_pub_fw: Firewall = network.get_node_by_hostname(hostname="ST-PUB-FW") + st_dmz_pub_srv_web: Server = network.get_node_by_hostname(hostname="ST-DMZ-PUB-SRV-WEB") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + + def ping_all_some_tech_dmz(host): + assert host.ping(st_dmz_pub_srv_web.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_dmz(host=st_pub_fw) + ping_all_some_tech_dmz(host=isp_pub_srv_dns) + ping_all_some_tech_dmz(host=home_pub_pc_1) + + +def test_ping_some_tech_head_office(uc7_network): + """Asserts that all the some_tech_* PCs can ping each other and the public dns""" + network = uc7_network + st_home_office_private_pc_1: Computer = network.get_node_by_hostname("ST-HO-PRV-PC-1") + st_home_office_private_pc_2: Computer = network.get_node_by_hostname("ST-HO-PRV-PC-2") + st_home_office_private_pc_3: Computer = network.get_node_by_hostname("ST-HO-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_head_office(host): + assert host.ping(st_home_office_private_pc_1.network_interface[1].ip_address) + assert host.ping(st_home_office_private_pc_2.network_interface[1].ip_address) + assert host.ping(st_home_office_private_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_head_office(host=st_home_office_private_pc_1) + ping_all_some_tech_head_office(host=st_home_office_private_pc_2) + ping_all_some_tech_head_office(host=st_home_office_private_pc_3) + + +def test_ping_some_tech_hr(uc7_network): + """Assert that all some_tech_hr_* PCs can ping each other and the public dns""" + network = uc7_network + some_tech_hr_pc_1: Computer = network.get_node_by_hostname("ST-HR-PRV-PC-1") + some_tech_hr_pc_2: Computer = network.get_node_by_hostname("ST-HR-PRV-PC-2") + some_tech_hr_pc_3: Computer = network.get_node_by_hostname("ST-HR-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_hr(host): + assert host.ping(some_tech_hr_pc_1.network_interface[1].ip_address) + assert host.ping(some_tech_hr_pc_2.network_interface[1].ip_address) + assert host.ping(some_tech_hr_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_hr(some_tech_hr_pc_1) + ping_all_some_tech_hr(some_tech_hr_pc_2) + ping_all_some_tech_hr(some_tech_hr_pc_3) + + +def test_some_tech_data_hr(uc7_network): + """Assert that all some_tech_data_* servers can ping each other and the public dns.""" + network = uc7_network + some_tech_data_server_storage: Server = network.get_node_by_hostname("ST-DATA-PRV-SRV-STORAGE") + some_tech_data_server_database: Server = network.get_node_by_hostname("ST-DATA-PRV-SRV-DB") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_hr(host): + assert host.ping(some_tech_data_server_storage.network_interface[1].ip_address) + assert host.ping(some_tech_data_server_database.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_hr(some_tech_data_server_storage) + ping_all_some_tech_hr(some_tech_data_server_database) + + +def test_some_tech_project_a(uc7_network): + """Asserts that all some_tech project A's PCs can ping each other and the public dns.""" + network = uc7_network + some_tech_proj_a_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-A-PRV-PC-1") + some_tech_proj_a_pc_2: Computer = network.get_node_by_hostname("ST-PROJ-A-PRV-PC-2") + some_tech_proj_a_pc_3: Computer = network.get_node_by_hostname("ST-PROJ-A-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_proj_a(host): + assert host.ping(some_tech_proj_a_pc_1.network_interface[1].ip_address) + assert host.ping(some_tech_proj_a_pc_2.network_interface[1].ip_address) + assert host.ping(some_tech_proj_a_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_proj_a(some_tech_proj_a_pc_1) + ping_all_some_tech_proj_a(some_tech_proj_a_pc_2) + ping_all_some_tech_proj_a(some_tech_proj_a_pc_3) + + +def test_some_tech_project_b(uc7_network): + """Asserts that all some_tech_project_b PC's can ping each other and the public dps.""" + network = uc7_network + some_tech_proj_b_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-B-PRV-PC-1") + some_tech_proj_b_pc_2: Computer = network.get_node_by_hostname("ST-PROJ-B-PRV-PC-2") + some_tech_proj_b_pc_3: Computer = network.get_node_by_hostname("ST-PROJ-B-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_proj_b(host): + assert host.ping(some_tech_proj_b_pc_1.network_interface[1].ip_address) + assert host.ping(some_tech_proj_b_pc_2.network_interface[1].ip_address) + assert host.ping(some_tech_proj_b_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_proj_b(some_tech_proj_b_pc_1) + ping_all_some_tech_proj_b(some_tech_proj_b_pc_2) + ping_all_some_tech_proj_b(some_tech_proj_b_pc_3) + + +def test_some_tech_project_a(uc7_network): + """Asserts that all some_tech_project_c PC's can ping each other and the public dps.""" + network = uc7_network + some_tech_proj_c_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-C-PRV-PC-1") + some_tech_proj_c_pc_2: Computer = network.get_node_by_hostname("ST-PROJ-C-PRV-PC-2") + some_tech_proj_c_pc_3: Computer = network.get_node_by_hostname("ST-PROJ-C-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_proj_c(host): + assert host.ping(some_tech_proj_c_pc_1.network_interface[1].ip_address) + assert host.ping(some_tech_proj_c_pc_2.network_interface[1].ip_address) + assert host.ping(some_tech_proj_c_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_proj_c(some_tech_proj_c_pc_1) + ping_all_some_tech_proj_c(some_tech_proj_c_pc_2) + ping_all_some_tech_proj_c(some_tech_proj_c_pc_3) + + +def test_ping_all_networks(uc7_network): + """Asserts that one machine from each network is able to ping all others.""" + network = uc7_network + home_office_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + remote_office_pc_1: Computer = network.get_node_by_hostname("REM-PUB-PC-1") + st_head_office_pc_1: Computer = network.get_node_by_hostname("ST-HO-PRV-PC-1") + st_human_resources_pc_1: Computer = network.get_node_by_hostname("ST-HR-PRV-PC-1") + st_data_storage_server: Server = network.get_node_by_hostname("ST-DATA-PRV-SRV-STORAGE") + st_data_database_server: Server = network.get_node_by_hostname("ST-DATA-PRV-SRV-DB") + st_proj_a_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-A-PRV-PC-1") + st_proj_b_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-B-PRV-PC-1") + st_proj_c_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-C-PRV-PC-1") + + def ping_network_wide(host): + assert host.ping(home_office_pc_1.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + assert host.ping(remote_office_pc_1.network_interface[1].ip_address) + assert host.ping(st_head_office_pc_1.network_interface[1].ip_address) + assert host.ping(st_human_resources_pc_1.network_interface[1].ip_address) + assert host.ping(st_data_storage_server.network_interface[1].ip_address) + assert host.ping(st_data_database_server.network_interface[1].ip_address) + assert host.ping(st_proj_a_pc_1.network_interface[1].ip_address) + assert host.ping(st_proj_b_pc_1.network_interface[1].ip_address) + assert host.ping(st_proj_c_pc_1.network_interface[1].ip_address) + + ping_network_wide(host=home_office_pc_1) + ping_network_wide(host=isp_pub_srv_dns) + ping_network_wide(host=remote_office_pc_1) + ping_network_wide(host=st_head_office_pc_1) + ping_network_wide(host=st_human_resources_pc_1) + ping_network_wide(host=st_data_storage_server) + ping_network_wide(host=st_data_database_server) + ping_network_wide(host=st_proj_a_pc_1) + ping_network_wide(host=st_proj_b_pc_1) + ping_network_wide(host=st_proj_c_pc_1) diff --git a/tests/e2e_integration_tests/test_uc7_services_and_applications.py b/tests/e2e_integration_tests/test_uc7_services_and_applications.py new file mode 100644 index 00000000..6514627c --- /dev/null +++ b/tests/e2e_integration_tests/test_uc7_services_and_applications.py @@ -0,0 +1,338 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK + +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.game import PrimaiteGame +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.network.container import Network +from primaite.simulator.network.hardware.nodes.host.computer import Computer +from primaite.simulator.network.hardware.nodes.host.server import Server +from primaite.simulator.system.applications.application import ApplicationOperatingState +from primaite.simulator.system.applications.database_client import DatabaseClient +from primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.system.services.database.database_service import DatabaseService +from primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.system.services.dns.dns_server import DNSServer +from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.ftp.ftp_server import FTPServer +from primaite.simulator.system.services.ntp.ntp_client import NTPClient +from primaite.simulator.system.services.ntp.ntp_server import NTPServer +from primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.system.software import SoftwareHealthState + +CONFIG_FILE = _EXAMPLE_CFG / "uc7_config.yaml" + + +@pytest.fixture(scope="function") +def uc7_network() -> Network: + with open(file=CONFIG_FILE, mode="r") as f: + cfg = yaml.safe_load(stream=f) + + game = PrimaiteGame.from_config(cfg=cfg) + return game.simulation.network + + +def assert_ntp_client(host): + """Confirms that the ntp_client service is present and functioning.""" + ntp_client: NTPClient = host.software_manager.software["ntp-client"] + assert ntp_client is not None + assert ntp_client.operating_state == ServiceOperatingState.RUNNING + assert ntp_client.health_state_actual == SoftwareHealthState.GOOD + + +def assert_dns_client(host): + """Confirms that the dns_client service is present and functioning.""" + dns_client: DNSClient = host.software_manager.software["dns-client"] + assert dns_client is not None + assert dns_client.operating_state == ServiceOperatingState.RUNNING + assert dns_client.health_state_actual == SoftwareHealthState.GOOD + + +def assert_web_browser(host: Computer): + """Asserts that the web_browser application is present and functioning.""" + web_browser: WebBrowser = host.software_manager.software["web-browser"] + assert web_browser is not None + assert web_browser.operating_state == ApplicationOperatingState.RUNNING + assert web_browser.health_state_actual == SoftwareHealthState.GOOD + + +def assert_database_client(host: Computer): + """Asserts that the database_client application is present and functioning.""" + database_client = host.software_manager.software["database-client"] + assert database_client is not None + assert database_client.operating_state == ApplicationOperatingState.RUNNING + assert database_client.health_state_actual == SoftwareHealthState.GOOD + + +def test_home_office_software(uc7_network): + """Asserts that each host in the home_office network contains the expected software.""" + network: Network = uc7_network + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + home_pub_pc_2: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + home_pub_srv: Server = network.get_node_by_hostname("HOME-PUB-SRV") + + # Home Office PC 1 + assert_web_browser(home_pub_pc_1) + assert_database_client(home_pub_pc_1) + assert_dns_client(home_pub_pc_1) + assert_ntp_client(home_pub_pc_1) + + # Home Office PC 2 + assert_web_browser(home_pub_pc_2) + assert_database_client(home_pub_pc_2) + assert_dns_client(home_pub_pc_2) + assert_ntp_client(home_pub_pc_2) + + # Home Office Server + assert_dns_client(home_pub_srv) + assert_ntp_client(home_pub_srv) + + +def test_internet_dns_server(uc7_network): + """Asserts that `ISP-PUB-SRV-DNS` host's DNSServer application is operating and functioning as expected.""" + network: Network = uc7_network + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + # Confirming that the DNSServer is up and running: + + dns_server: DNSServer = isp_pub_srv_dns.software_manager.software["dns-server"] + assert dns_server is not None + assert dns_server.operating_state == ServiceOperatingState.RUNNING + assert dns_server.health_state_actual == SoftwareHealthState.GOOD + + # Confirming that the DNSServer is performing as expected by performing a request from a client + + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + dns_client: DNSClient = home_pub_pc_1.software_manager.software["dns-client"] + + assert dns_client.check_domain_exists(target_domain="some_tech.com") + assert dns_client.dns_cache.get("some_tech.com", None) is not None + assert len(dns_client.dns_cache) == 1 + + +def test_remote_office_software(uc7_network): + """Asserts that each host on the remote_office network has the expected services & applications which are operating as expected.""" + network = uc7_network + rem_pub_pc_1: Computer = network.get_node_by_hostname(hostname="REM-PUB-PC-1") + rem_pub_pc_2: Computer = network.get_node_by_hostname(hostname="REM-PUB-PC-2") + rem_pub_srv: Server = network.get_node_by_hostname(hostname="REM-PUB-SRV") + + # Remote Site PC 1 + assert_web_browser(rem_pub_pc_1) + assert_database_client(rem_pub_pc_1) + assert_dns_client(rem_pub_pc_1) + assert_ntp_client(rem_pub_pc_1) + + # Remote Site PC 2 + assert_web_browser(rem_pub_pc_2) + assert_database_client(rem_pub_pc_2) + assert_dns_client(rem_pub_pc_2) + assert_ntp_client(rem_pub_pc_2) + + # Remote Site Server + assert_dns_client(rem_pub_srv) + assert_ntp_client(rem_pub_srv) + + +def test_dmz_web_server(uc7_network): + """Asserts that the DMZ WebServer functions as expected""" + network: Network = uc7_network + st_dmz_pub_srv_web: Server = network.get_node_by_hostname("ST-DMZ-PUB-SRV-WEB") + + # Asserting the ST Web Server is working as expected + st_web_server = st_dmz_pub_srv_web.software_manager.software["web-server"] + assert st_web_server is not None + assert st_web_server.operating_state == ServiceOperatingState.RUNNING + assert st_web_server.health_state_actual == SoftwareHealthState.GOOD + + # Asserting that WebBrowser can actually connect to the WebServer + + # SOME TECH Human Resources --> DMZ Web Server + st_hr_pc_1: Computer = network.get_node_by_hostname("ST-HR-PRV-PC-1") + st_hr_pc_1_web_browser: WebBrowser = st_hr_pc_1.software_manager.software["web-browser"] + assert st_hr_pc_1_web_browser.get_webpage("http://some_tech.com") + + # Remote Site --> DMZ Web Server + rem_pub_pc_1: Computer = network.get_node_by_hostname("REM-PUB-PC-1") + rem_pub_pc_1_web_browser: WebBrowser = rem_pub_pc_1.software_manager.software["web-browser"] + assert rem_pub_pc_1_web_browser.get_webpage("http://some_tech.com") + + # Home Office --> DMZ Web Server + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + home_pub_pc_1_web_browser: WebBrowser = home_pub_pc_1.software_manager.software["web-browser"] + assert home_pub_pc_1_web_browser.get_webpage("http://some_tech.com") + + +def test_tech_head_office_software(uc7_network): + """Asserts that each host on the some_tech_head_office network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + + st_head_office_private_pc_1: Computer = network.get_node_by_hostname("ST-HO-PRV-PC-1") + st_head_office_private_pc_2: Computer = network.get_node_by_hostname("ST-HO-PRV-PC-2") + st_head_office_private_pc_3: Computer = network.get_node_by_hostname("ST-HO-PRV-PC-3") + + # ST Head Office One + + assert_web_browser(st_head_office_private_pc_1) + assert_database_client(st_head_office_private_pc_1) + assert_dns_client(st_head_office_private_pc_1) + assert_ntp_client(st_head_office_private_pc_1) + + # ST Head Office Two + + assert_web_browser(st_head_office_private_pc_2) + assert_database_client(st_head_office_private_pc_2) + assert_dns_client(st_head_office_private_pc_2) + assert_ntp_client(st_head_office_private_pc_2) + + # ST Head Office Three + + assert_web_browser(st_head_office_private_pc_3) + assert_database_client(st_head_office_private_pc_3) + assert_dns_client(st_head_office_private_pc_3) + assert_ntp_client(st_head_office_private_pc_3) + + +def test_tech_human_resources_office_software(uc7_network): + """Asserts that each host on the some_tech human_resources network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + + st_hr_pc_1: Computer = network.get_node_by_hostname("ST-HR-PRV-PC-1") + st_hr_pc_2: Computer = network.get_node_by_hostname("ST-HR-PRV-PC-2") + st_hr_pc_3: Computer = network.get_node_by_hostname("ST-HR-PRV-PC-3") + + # ST Human Resource PC 1 + + assert_web_browser(st_hr_pc_1) + assert_database_client(st_hr_pc_1) + assert_dns_client(st_hr_pc_1) + assert_ntp_client(st_hr_pc_1) + + # ST Human Resource PC 2 + + assert_web_browser(st_hr_pc_2) + assert_database_client(st_hr_pc_2) + assert_dns_client(st_hr_pc_2) + assert_ntp_client(st_hr_pc_2) + + # ST Human Resource PC 3 + + assert_web_browser(st_hr_pc_3) + assert_database_client(st_hr_pc_3) + assert_dns_client(st_hr_pc_3) + assert_ntp_client(st_hr_pc_3) + + +def test_tech_data_software(uc7_network): + """Asserts the database and database storage servers on the some_tech data network are operating as expected.""" + network: Network = uc7_network + st_data_database_server: Server = network.get_node_by_hostname("ST-DATA-PRV-SRV-DB") + st_data_database_storage: Server = network.get_node_by_hostname("ST-DATA-PRV-SRV-STORAGE") + st_proj_a_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-A-PRV-PC-1") + + # Asserting that the database_service is working as expected + database_service: DatabaseService = st_data_database_server.software_manager.software["database-service"] + + assert database_service is not None + assert database_service.operating_state == ServiceOperatingState.RUNNING + assert database_service.health_state_actual == SoftwareHealthState.GOOD + + # Asserting that the database_client can connect to the database + database_client: DatabaseClient = st_proj_a_pc_1.software_manager.software["database-client"] + + assert database_client.server_ip_address is not None + assert database_client.server_ip_address == st_data_database_server.network_interface[1].ip_address + assert database_client.connect() + + # Asserting that the database storage works as expected. + assert database_service.backup_server_ip == st_data_database_storage.network_interface[1].ip_address + assert database_service.backup_database() + + +def test_tech_proj_a_software(uc7_network): + """Asserts that each host on the some_tech project A network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + st_proj_a_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-A-PRV-PC-1") + st_proj_a_pc_2: Computer = network.get_node_by_hostname("ST-PROJ-A-PRV-PC-2") + st_proj_a_pc_3: Computer = network.get_node_by_hostname("ST-PROJ-A-PRV-PC-3") + + # ST Project A - PC 1 + + assert_web_browser(st_proj_a_pc_1) + assert_database_client(st_proj_a_pc_1) + assert_dns_client(st_proj_a_pc_1) + assert_ntp_client(st_proj_a_pc_1) + + # ST Project A - PC 2 + + assert_web_browser(st_proj_a_pc_2) + assert_database_client(st_proj_a_pc_2) + assert_dns_client(st_proj_a_pc_2) + assert_ntp_client(st_proj_a_pc_2) + + # ST Project A - PC 3 + + assert_web_browser(st_proj_a_pc_3) + assert_database_client(st_proj_a_pc_3) + assert_dns_client(st_proj_a_pc_3) + assert_ntp_client(st_proj_a_pc_3) + + +def test_tech_proj_b_software(uc7_network): + """Asserts that each host on the some_tech project A network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + st_proj_b_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-B-PRV-PC-1") + st_proj_b_pc_2: Computer = network.get_node_by_hostname("ST-PROJ-B-PRV-PC-2") + st_proj_b_pc_3: Computer = network.get_node_by_hostname("ST-PROJ-B-PRV-PC-3") + + # ST Project B - PC 1 + + assert_web_browser(st_proj_b_pc_1) + assert_database_client(st_proj_b_pc_1) + assert_dns_client(st_proj_b_pc_1) + assert_ntp_client(st_proj_b_pc_1) + + # ST Project B - PC2 + + assert_web_browser(st_proj_b_pc_2) + assert_database_client(st_proj_b_pc_2) + assert_dns_client(st_proj_b_pc_2) + assert_ntp_client(st_proj_b_pc_2) + + # ST Project B - PC3 + + assert_web_browser(st_proj_b_pc_3) + assert_database_client(st_proj_b_pc_3) + assert_dns_client(st_proj_b_pc_3) + assert_ntp_client(st_proj_b_pc_3) + + +def test_tech_proj_c_software(uc7_network): + """Asserts that each host on the some_tech project A network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + st_proj_c_pc_1: Computer = network.get_node_by_hostname("ST-PROJ-C-PRV-PC-1") + st_proj_c_pc_2: Computer = network.get_node_by_hostname("ST-PROJ-C-PRV-PC-2") + st_proj_c_pc_3: Computer = network.get_node_by_hostname("ST-PROJ-C-PRV-PC-3") + + # ST Project C - PC 1 + + assert_web_browser(st_proj_c_pc_1) + assert_database_client(st_proj_c_pc_1) + assert_dns_client(st_proj_c_pc_1) + assert_ntp_client(st_proj_c_pc_1) + + # ST Project C - PC2 + + assert_web_browser(st_proj_c_pc_2) + assert_database_client(st_proj_c_pc_2) + assert_dns_client(st_proj_c_pc_2) + assert_ntp_client(st_proj_c_pc_2) + + # ST Project C - PC3 + + assert_web_browser(st_proj_c_pc_3) + assert_database_client(st_proj_c_pc_3) + assert_dns_client(st_proj_c_pc_3) + assert_ntp_client(st_proj_c_pc_3) diff --git a/tests/e2e_integration_tests/threat_actor_profiles/__init__.py b/tests/e2e_integration_tests/threat_actor_profiles/__init__.py new file mode 100644 index 00000000..836b79af --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/__init__.py @@ -0,0 +1 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_abstract_tap.py b/tests/e2e_integration_tests/threat_actor_profiles/test_abstract_tap.py new file mode 100644 index 00000000..0135a4fb --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_abstract_tap.py @@ -0,0 +1,143 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain +from primaite.session.environment import PrimaiteGymEnv + + +def uc7_tap001_env() -> PrimaiteGymEnv: + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + + for a in cfg["agents"]: + if a["ref"] == "attacker": + tap_cfg = a + + tap_cfg["agent_settings"]["start_step"] = 1 + tap_cfg["agent_settings"]["frequency"] = 5 + tap_cfg["agent_settings"]["variance"] = 0 + + env = PrimaiteGymEnv(env_config=cfg) + + return env + + +def uc7_tap003_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with the following settings: + + start_step = Start on Step 1 + frequency = Attack Every 5 Steps + + Each PyTest will define the rest of the TAP & Kill Chain settings via **Kwargs + """ + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", "r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + + for a in cfg["agents"]: + if a["ref"] == "attacker": + tap_cfg = a + + tap_cfg["agent_settings"]["start_step"] = 1 + tap_cfg["agent_settings"]["frequency"] = 5 + tap_cfg["agent_settings"]["variance"] = 0 + + if "repeat_kill_chain" in kwargs: + tap_cfg["agent_settings"]["repeat_kill_chain"] = kwargs["repeat_kill_chain"] + if "repeat_kill_chain_stages" in kwargs: + tap_cfg["agent_settings"]["repeat_kill_chain_stages"] = kwargs["repeat_kill_chain_stages"] + if "planning_probability" in kwargs: + tap_cfg["agent_settings"]["kill_chain"]["PLANNING"]["probability"] = kwargs["planning_probability"] + if "custom_kill_chain" in kwargs: + tap_cfg["agent_settings"]["kill_chain"] = kwargs["custom_kill_chain"] + if "starting_nodes" in kwargs: + tap_cfg["agent_settings"]["starting_nodes"] = kwargs["starting_nodes"] + if "target_nodes" in kwargs: + tap_cfg["agent_settings"]["target_nodes"] = kwargs["target_nodes"] + + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap001_setup(): + """Tests abstract TAP's following methods: + 1. _setup_kill_chain + 2. _setup_agent_kill_chain + 3. _setup_tap_applications + """ + env = uc7_tap001_env() # Using TAP001 for PyTests. + tap: TAP001 = env.game.agents["attacker"] + + # check the kill chain loaded correctly + assert tap.selected_kill_chain is MobileMalwareKillChain + assert tap.selected_kill_chain.FAILED == BaseKillChain.FAILED + assert tap.selected_kill_chain.SUCCEEDED == BaseKillChain.SUCCEEDED + assert tap.selected_kill_chain.NOT_STARTED == BaseKillChain.NOT_STARTED + + if sn := tap.config.agent_settings.default_starting_node: + assert tap.starting_node == sn + else: + assert tap.starting_node in tap.config.agent_settings.starting_nodes + + if ti := tap.config.agent_settings.default_target_ip: + assert tap.target_ip == ti + else: + assert tap.target_ip in tap.config.agent_settings.target_ips + + assert tap.next_execution_timestep == tap.config.agent_settings.start_step + + assert tap.current_host == tap.starting_node + + +def test_abstract_tap_select_start_node(): + """Tests that Abstract TAP's _select_start_node""" + env = uc7_tap003_env(repeat_kill_chain=True, repeat_kill_chain_stages=True) # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + + assert tap.starting_node == "ST-PROJ-A-PRV-PC-1" + assert tap.current_host == tap.starting_node + + +def test_outcome_handler(): + """Tests Abstract Tap's outcome handler concludes the episode when the kill chain fails.""" + env = uc7_tap003_env(repeat_kill_chain=False, repeat_kill_chain_stages=False) # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + tap.current_kill_chain_stage = BaseKillChain.FAILED + + for _ in range(6): + env.step(0) + assert tap.actions_concluded == True + + +def test_abstract_tap_kill_chain_repeat(): + """Tests that the kill chain repeats from the beginning upon failure.""" + env = uc7_tap003_env(repeat_kill_chain=True, repeat_kill_chain_stages=False) # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + for _ in range(15): + env.step(0) # Steps directly to the Access Stage + assert tap.current_kill_chain_stage == InsiderKillChain.ACCESS + tap.current_kill_chain_stage = BaseKillChain.FAILED + for _ in range(5): + env.step(0) # Steps to manipulation - but failure causes the kill chain to restart. + assert tap.actions_concluded == False + assert tap.current_kill_chain_stage == InsiderKillChain.RECONNAISSANCE + + """Tests that kill chain stages repeat when expected""" + env = uc7_tap003_env( + repeat_kill_chain=True, repeat_kill_chain_stages=True, planning_probability=0 + ) # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + tap.current_kill_chain_stage = InsiderKillChain.PLANNING + for _ in range(15): + env.step(0) # Attempts to progress past the PLANNING stage multiple times. + assert tap.actions_concluded == False + assert tap.current_kill_chain_stage == InsiderKillChain.PLANNING diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_kill_chain_methods.py b/tests/e2e_integration_tests/threat_actor_profiles/test_kill_chain_methods.py new file mode 100644 index 00000000..66279e6c --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_kill_chain_methods.py @@ -0,0 +1,69 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain +from primaite.session.environment import PrimaiteGymEnv + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) + + +def uc7_tap003_env() -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with the start_step & frequency set to 1 with probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][32]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][32]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][32]["agent_settings"]["variance"] = VARIANCE + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def uc7_tap001_env() -> PrimaiteGymEnv: + """Setup the UC7 TAP001 Game with the start_step & frequency set to 1 & 2 respectively. Probabilities are set to 1""" + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][32]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][32]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][32]["agent_settings"]["variance"] = VARIANCE + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap003_insider_kill_chain_load(): + """Tests that tap003's insider kill chain is successfully loaded into the tap.selected_kill_chain attribute.""" + env = uc7_tap003_env() # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + # Asserting that the Base Kill Chain intEnum stages are loaded + assert BaseKillChain.FAILED in [enums for enums in tap.selected_kill_chain] + assert BaseKillChain.SUCCEEDED in [enums for enums in tap.selected_kill_chain] + assert BaseKillChain.NOT_STARTED in [enums for enums in tap.selected_kill_chain] + # Asserting that the Insider Kill Chain intenum stages are loaded. + assert len(InsiderKillChain.__members__) == len(tap.selected_kill_chain.__members__) + + +def test_tap001_mobile_malware_kill_chain_load(): + """Tests that tap001's mobile malware is successfully loaded into the tap.selected_kill_chain attribute.""" + env = uc7_tap001_env() # Using TAP003 for PyTests. + tap: TAP001 = env.game.agents["attacker"] + # Asserting that the Base Kill Chain intEnum stages are loaded. + assert BaseKillChain.FAILED in [enums for enums in tap.selected_kill_chain] + assert BaseKillChain.SUCCEEDED in [enums for enums in tap.selected_kill_chain] + assert BaseKillChain.NOT_STARTED in [enums for enums in tap.selected_kill_chain] + # Asserting that the Insider Kill Chain intEnum stages are loaded. + assert len(MobileMalwareKillChain.__members__) == len(tap.selected_kill_chain.__members__) diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_repeat.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_repeat.py new file mode 100644 index 00000000..ec3ee1bf --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_repeat.py @@ -0,0 +1,109 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain +from primaite.session.environment import PrimaiteGymEnv + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) + + +def uc7_tap001_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 tap001 Game with the start_step & frequency set to 1 with probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][32]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][32]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][32]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][32]["agent_settings"]["repeat_kill_chain"] = kwargs["repeat_kill_chain"] + cfg["agents"][32]["agent_settings"]["repeat_kill_chain_stages"] = kwargs["repeat_kill_chain_stages"] + cfg["agents"][32]["agent_settings"]["kill_chain"]["PROPAGATE"]["probability"] = kwargs["propagate_probability"] + cfg["agents"][32]["agent_settings"]["kill_chain"]["PAYLOAD"]["probability"] = kwargs["payload_probability"] + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap001_repeating_kill_chain(): + """Tests to check that tap001 repeats it's kill chain after success""" + env = uc7_tap001_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + payload_probability=1, + propagate_probability=1, + ) + tap001: TAP001 = env.game.agents["attacker"] + # Looping for 50 timesteps - As the agent is set to execute an action every 2 timesteps + # This is the equivalent of the agent taking 20 actions. + for _ in range(50): # This for loop should never actually fully complete. + if tap001.current_kill_chain_stage == BaseKillChain.SUCCEEDED: + break + env.step(0) + + # Catches if the above for loop fully completes. + # This test uses a probability of 1 for all stages and a variance of 2 timesteps + # Thus the for loop above should never fail. + # If this occurs then there is an error somewhere in either: + # 1. The TAP Logic + # 2. Failing Agent Actions are causing the TAP to fail. (See tap_return_handler). + if tap001.current_kill_chain_stage != BaseKillChain.SUCCEEDED: + pytest.fail("Attacker Never Reached SUCCEEDED - Please evaluate current TAP Logic.") + + # Stepping twice for the succeeded logic to kick in: + env.step(0) + env.step(0) + env.step(0) + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.DOWNLOAD.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + + +def test_tap001_repeating_kill_chain_stages(): + """Tests to check that tap001 repeats it's kill chain after failing a kill chain stage.""" + env = uc7_tap001_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + payload_probability=1, + propagate_probability=0, + # Probability 0 = Will never be able to perform the access stage and progress to Manipulation. + ) + tap001: TAP001 = env.game.agents["attacker"] + env.step(0) # Skipping not started + env.step(0) # Successful on the first stage + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.DOWNLOAD.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + env.step(0) # Successful progression to the second stage + env.step(0) + env.step(0) + env.step(0) + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.ACTIVATE.name + env.step(0) # Successful progression to the third stage + env.step(0) + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.ACTIVATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + env.step(0) # Successful progression to the Fourth stage + env.step(0) + env.step(0) + env.step(0) + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + env.step(0) # FAILURE -- Unsuccessful progression to the Fourth stage + env.step(0) + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + assert tap001.current_stage_progress == KillChainStageProgress.PENDING diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_stages.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_stages.py new file mode 100644 index 00000000..17fe9b25 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_stages.py @@ -0,0 +1,215 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.system.applications.red_applications.c2.c2_beacon import C2Beacon +from primaite.simulator.system.services.database.database_service import DatabaseService + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +REPEAT_KILL_CHAIN = False # Should the TAP repeat the kill chain after success/failure? +REPEAT_KILL_CHAIN_STAGES = False # Should the TAP restart from it's previous stage on failure? +KILL_CHAIN_PROBABILITY = 1 # Blank probability for agent 'success' +DATA_EXFIL = True # Data exfiltration on the payload stage is enabled. + + +def uc7_tap001_env() -> PrimaiteGymEnv: + """Setups the UC7 tap001 Game with the start_step & frequency set to 1 with probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][32]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][32]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][32]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][32]["agent_settings"]["repeat_kill_chain"] = REPEAT_KILL_CHAIN_STAGES + cfg["agents"][32]["agent_settings"]["repeat_kill_chain_stages"] = REPEAT_KILL_CHAIN_STAGES + cfg["agents"][32]["agent_settings"]["kill_chain"]["PAYLOAD"]["probability"] = KILL_CHAIN_PROBABILITY + cfg["agents"][32]["agent_settings"]["kill_chain"]["PROPAGATE"]["probability"] = KILL_CHAIN_PROBABILITY + cfg["agents"][32]["agent_settings"]["kill_chain"]["PAYLOAD"]["exfiltrate"] = DATA_EXFIL + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap001_kill_chain_stage_DOWNLOAD(): + """Tests that the DOWNLOAD Mobile Malware step works as expected and the expected impacts are made in the simulation.""" + + # Instantiating the relevant simulation/game objects: + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + assert tap001.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + # Frequency is set to two steps + env.step(0) + env.step(0) + + env.step(0) + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.DOWNLOAD.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + assert tap001.current_stage_progress == KillChainStageProgress.IN_PROGRESS + + # Creating the "downloads" folder + env.step(0) + env.step(0) + + assert starting_host.software_manager.file_system.get_folder(folder_name="downloads") + assert starting_host.software_manager.file_system.get_file(folder_name="downloads", file_name="malware_dropper.ps1") + + # Testing that the num_file_increase works + + assert starting_host.file_system.num_file_creations == 1 + + +def test_tap001_kill_chain_stage_INSTALL(): + """Tests that the INSTALL Mobile Malware step works as expected and the expected impacts are made in the simulation.""" + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + # The tap001's Starting Client: + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + + # Skipping directly to the activate stage + for _ in range(6): + env.step(0) + + # Testing that tap001 Enters into the expected kill chain stages + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.ACTIVATE.name + + env.step(0) # Allows the agent action to resolve. + env.step(0) + + ransomware_dropper_file = starting_host.software_manager.file_system.get_file( + folder_name="downloads", file_name="malware_dropper.ps1" + ) + assert ransomware_dropper_file.num_access == 1 + + +def test_tap001_kill_chain_stage_ACTIVATE(): + """Tests that the ACTIVATE Mobile Malware step works as expected and the current impacts are made in the simulation.""" + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + # The tap001's Starting Client: + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + + # Skipping directly to the activate stage + for _ in range(8): + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.ACTIVATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + + # Installing ransomware-script Application + env.step(0) + env.step(0) + + # Installing NMAP Application + env.step(0) + env.step(0) + + # These asserts will fail if the applications are not present in the software_manager + assert starting_host.software_manager.software["ransomware-script"] + assert starting_host.software_manager.software["nmap"] + + +def test_tap001_kill_chain_stage_PROPAGATE(): + """Tests that the ACTIVATE Mobile Malware step works as expected and the current impacts are made in the simulation.""" + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + + for _ in range(12): + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + + # Specific Stage by Stage Propagate Testing is done in test_tap001_propagate. + fail_safe_var = 0 + while tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE: + env.step(0) + assert tap001.current_stage_progress == KillChainStageProgress.IN_PROGRESS + fail_safe_var += 1 + if fail_safe_var == 100: + pytest.fail("Fail Safe Variable was hit! -- Propagate step is running indefinitely") + + +def test_tap001_kill_chain_stage_COMMAND_AND_CONTROL(): + """Tests that the Command And Control Mobile Malware step works as expected and the current impacts are made in the simulation.""" + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + fail_safe_var = 0 + + for _ in range(28): + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PAYLOAD.name + + while tap001.current_kill_chain_stage == MobileMalwareKillChain.COMMAND_AND_CONTROL: + env.step(0) + fail_safe_var += 1 + env.game.simulation.network.airspace.show() + if fail_safe_var == 100: + pytest.fail(reason="Fail Safe Variable was hit! -- Propagate step is running indefinitely") + + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + + c2_beacon: C2Beacon = starting_host.software_manager.software["c2-beacon"] + + assert c2_beacon.c2_connection_active is True + + +def test_tap001_kill_chain_stage_PAYLOAD(): + """Tests that the PAYLOAD Mobile Malware step works as expected and the current impacts are made in the simulation.""" + + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + + # The tap001's Target Database + target_host = env.game.simulation.network.get_node_by_hostname("ST-DATA-PRV-SRV-DB") + db_server_service: DatabaseService = target_host.software_manager.software.get("database-service") + + # Green agent status requests are tested within the ransomware application tests. + # See test_ransomware_disrupts_green_agent_connection for further reference. + assert db_server_service.db_file.health_status is FileSystemItemHealthStatus.GOOD + + fail_safe_var = 0 + while tap001.current_kill_chain_stage != MobileMalwareKillChain.PAYLOAD: + env.step(0) + fail_safe_var += 1 + if fail_safe_var == 100: + pytest.fail(reason="Fail Safe Variable was hit! -- a step is running indefinitely") + + for _ in range(12): + env.step(0) + + assert db_server_service.db_file.health_status is FileSystemItemHealthStatus.CORRUPT + + # Asserting we've managed to the database.db file onto the starting node & server + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + c2_host = env.game.simulation.network.get_node_by_hostname(tap001.c2_settings["c2_server"]) + + assert starting_host.file_system.access_file(folder_name="exfiltration_folder", file_name="database.db") + assert c2_host.file_system.access_file(folder_name="exfiltration_folder", file_name="database.db") diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_propagate_stage.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_propagate_stage.py new file mode 100644 index 00000000..13b849c9 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_propagate_stage.py @@ -0,0 +1,140 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.system.applications.red_applications.c2.c2_beacon import C2Beacon +from primaite.simulator.system.services.database.database_service import DatabaseService + +# Defining generic tap constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +REPEAT_KILL_CHAIN = False # Should the TAP repeat the kill chain after success/failure? +REPEAT_KILL_CHAIN_STAGES = False # Should the TAP restart from it's previous stage on failure? +KILL_CHAIN_PROBABILITY = 1 # Blank probability for agent 'success' + + +def uc7_tap001_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 tap001 Game with the start_step & frequency set to 1 with probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + agent_cfg = cfg["agents"][32]["agent_settings"] + agent_cfg["start_step"] = START_STEP + agent_cfg["frequency"] = FREQUENCY + agent_cfg["variance"] = VARIANCE + agent_cfg["repeat_kill_chain"] = REPEAT_KILL_CHAIN_STAGES + agent_cfg["repeat_kill_chain_stages"] = REPEAT_KILL_CHAIN_STAGES + agent_cfg["kill_chain"]["PAYLOAD"]["probability"] = KILL_CHAIN_PROBABILITY + agent_cfg["kill_chain"]["PROPAGATE"]["probability"] = KILL_CHAIN_PROBABILITY + agent_cfg["kill_chain"]["PROPAGATE"]["scan_attempts"] = kwargs["scan_attempts"] + agent_cfg["kill_chain"]["PAYLOAD"]["payload"] = kwargs["payload"] + agent_cfg["kill_chain"]["PROPAGATE"]["network_addresses"] = kwargs["network_addresses"] + if "repeat_scan" in kwargs: + agent_cfg["kill_chain"]["PROPAGATE"]["repeat_scan"] = kwargs["repeat_scan"] + if "starting_nodes" in kwargs: + agent_cfg["starting_nodes"] = kwargs["starting_nodes"] + agent_cfg["default_starting_node"] = kwargs["starting_nodes"][0] + if "target_ips" in kwargs: + agent_cfg["target_ips"] = kwargs["target_ips"] + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap001_kill_chain_stage_PROPAGATE_default(): + """Tests that the PROPAGATE Mobile Malware step works as expected and the current impacts are made in the simulation.""" + payload = "ENCRYPT" + scan_attempts = 10 + network_addresses = [ + "192.168.230.0/29", + "192.168.10.0/26", + "192.168.20.0/30", + "192.168.240.0/29", + "192.168.220.0/29", + ] + env = uc7_tap001_env(payload=payload, scan_attempts=scan_attempts, network_addresses=network_addresses) + tap001: TAP001 = env.game.agents["attacker"] + + # First Kill Chain Stages + for _ in range(12): + env.step(0) + + # Assert that we're about to enter into the propagate stage. + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + + # Move into the propagate stage. + while tap001.current_kill_chain_stage == MobileMalwareKillChain.PROPAGATE: + env.step(0) + + # Assert that we've successfully moved into the command and control stage. + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PAYLOAD.name + + +def test_tap001_kill_chain_stage_PROPAGATE_different_starting_node(): + """Tests that the PROPAGATE Mobile Malware step works as expected and the current impacts are made in the simulation from a different starting node.""" + payload = "ENCRYPT" + scan_attempts = 10 + network_addresses = [ + "192.168.230.0/29", + "192.168.10.0/26", + "192.168.20.0/30", + "192.168.240.0/29", + "192.168.220.0/29", + ] + starting_nodes = ["ST-PROJ-B-PRV-PC-2", "ST-PROJ-C-PRV-PC-3"] + + env = uc7_tap001_env( + payload=payload, scan_attempts=scan_attempts, network_addresses=network_addresses, starting_nodes=starting_nodes + ) + + tap001: TAP001 = env.game.agents["attacker"] + + for _ in range(12): + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + + # Specific Stage by Stage Propagate Testing is done in test_tap001_propagate. + while tap001.current_kill_chain_stage == MobileMalwareKillChain.PROPAGATE: + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PAYLOAD.name + + +def test_tap001_kill_chain_stage_PROPAGATE_repeat_scan(): + """Tests that the PROPAGATE Mobile Malware step will fail when the target is unable to be located.""" + payload = "ENCRYPT" + scan_attempts = 20 + repeat_scan = True + network_addresses = ["192.168.1.0/24", "192.168.0.0/28", "100.64.0.0/30", "172.168.0.0/28"] + env = uc7_tap001_env( + payload=payload, scan_attempts=scan_attempts, network_addresses=network_addresses, repeat_scan=repeat_scan + ) + for _ in range(12): + env.step(0) + + tap001: TAP001 = env.game.agents["attacker"] + + while tap001.current_kill_chain_stage == MobileMalwareKillChain.PROPAGATE: + env.step(0) + + # As the given network_address does not contain the target, we should failed because the maximum amount of scan attempts has been reached + assert tap001.scans_complete == 20 + assert tap001.current_kill_chain_stage == MobileMalwareKillChain.FAILED diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_repeat.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_repeat.py new file mode 100644 index 00000000..42b33a7e --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_repeat.py @@ -0,0 +1,101 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain +from primaite.session.environment import PrimaiteGymEnv + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) + + +def uc7_tap003_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with the start_step & frequency set to 1 with probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][32]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][32]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][32]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][32]["agent_settings"]["repeat_kill_chain"] = kwargs["repeat_kill_chain"] + cfg["agents"][32]["agent_settings"]["repeat_kill_chain_stages"] = kwargs["repeat_kill_chain_stages"] + cfg["agents"][32]["agent_settings"]["kill_chain"]["MANIPULATION"]["probability"] = kwargs[ + "manipulation_probability" + ] + cfg["agents"][32]["agent_settings"]["kill_chain"]["ACCESS"]["probability"] = kwargs["access_probability"] + cfg["agents"][32]["agent_settings"]["kill_chain"]["PLANNING"]["probability"] = kwargs["planning_probability"] + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap003_repeating_kill_chain(): + """Tests to check that TAP003 repeats it's kill chain after success""" + env = uc7_tap003_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + manipulation_probability=1, + access_probability=1, + planning_probability=1, + ) + tap003: TAP003 = env.game.agents["attacker"] + for _ in range(40): # This for loop should never actually fully complete. + if tap003.current_kill_chain_stage == BaseKillChain.SUCCEEDED: + break + env.step(0) + + # Catches if the above for loop fully completes. + # This test uses a probability of 1 for all stages and a variance of 2 timesteps + # Thus the for loop above should never fail. + # If this occurs then there is an error somewhere in either: + # 1. The TAP Logic + # 2. Failing Agent Actions are causing the TAP to fail. (See tap_return_handler). + if tap003.current_kill_chain_stage != BaseKillChain.SUCCEEDED: + pytest.fail("Attacker Never Reached SUCCEEDED - Please evaluate current TAP Logic.") + + # Stepping twice for the succeeded logic to kick in: + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + +def test_tap003_repeating_kill_chain_stages(): + """Tests to check that TAP003 repeats it's kill chain after failing a kill chain stage.""" + env = uc7_tap003_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + manipulation_probability=1, + # Probability 0 = Will never be able to perform the access stage and progress to Manipulation. + access_probability=0, + planning_probability=1, + ) + tap003: TAP003 = env.game.agents["attacker"] + env.step(0) # Skipping not started + env.step(0) # Successful on the first stage + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + env.step(0) # Successful progression to the second stage + env.step(0) + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + env.step(0) # Successfully moved onto access. + env.step(0) + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + env.step(0) # Failure to progress past the third stage. + env.step(0) + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_stages.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_stages.py new file mode 100644 index 00000000..e9410ee4 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_stages.py @@ -0,0 +1,232 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.network.hardware.nodes.network.router import ACLAction + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +REPEAT_KILL_CHAIN = False # Should the TAP repeat the kill chain after success/failure? +REPEAT_KILL_CHAIN_STAGES = False # Should the TAP restart from it's previous stage on failure? +KILL_CHAIN_PROBABILITY = 1 # Blank probability for agent 'success' + + +def uc7_tap003_env() -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with the start_step & frequency set to 1 with probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][32]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][32]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][32]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][32]["agent_settings"]["repeat_kill_chain"] = REPEAT_KILL_CHAIN_STAGES + cfg["agents"][32]["agent_settings"]["repeat_kill_chain_stages"] = REPEAT_KILL_CHAIN_STAGES + cfg["agents"][32]["agent_settings"]["kill_chain"]["MANIPULATION"]["probability"] = KILL_CHAIN_PROBABILITY + cfg["agents"][32]["agent_settings"]["kill_chain"]["ACCESS"]["probability"] = KILL_CHAIN_PROBABILITY + cfg["agents"][32]["agent_settings"]["kill_chain"]["PLANNING"]["probability"] = KILL_CHAIN_PROBABILITY + cfg["agents"][32]["agent_settings"]["kill_chain"]["EXPLOIT"]["probability"] = KILL_CHAIN_PROBABILITY + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def environment_step(i: int, env: PrimaiteGymEnv) -> PrimaiteGymEnv: + """Carries out i (given parameter) steps in the environment..""" + for x in range(i): + env.step(0) + return env + + +def test_tap003_kill_chain_stage_reconnaissance(): + """Tests the successful/failed handlers in the reconnaissance stage in the Insider Kill Chain InsiderKillChain""" + + # Instantiating the relevant simulation/game objects: + env = uc7_tap003_env() + tap003: TAP003 = env.game.agents["attacker"] + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + # Frequency is set to two steps + env = environment_step(i=2, env=env) + + # Testing that TAP003 Enters into the expected kill chain stages + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + + +def test_tap003_kill_chain_stage_planning(): + """Tests the successful/failed handlers in the planning stage in the Insider Kill Chain (TAP003)""" + env = uc7_tap003_env() + tap003: TAP003 = env.game.agents["attacker"] + + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + env = environment_step(i=2, env=env) + + # Testing that TAP003 Enters into the expected kill chain stages + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + + env = environment_step(i=2, env=env) + + # Testing that the stage successfully impacted the simulation - User is logged in + # TODO: Add an assert for this. + + +def test_tap003_kill_chain_stage_access(): + """Tests the successful/failed handlers in the access stage in the InsiderKillChain""" + env = uc7_tap003_env() + tap003: TAP003 = env.game.agents["attacker"] + + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + + env = environment_step(i=2, env=env) + + +def test_tap003_kill_chain_stage_manipulation(): + """Tests the successful/failed handlers in the manipulation stage in the InsiderKillChain""" + + env = uc7_tap003_env() + env.reset() + tap003: TAP003 = env.game.agents["attacker"] + + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + + # Testing that the stage successfully impacted the simulation - Accounts Altered + env.step(0) + env.step(0) + env.step(0) + env.step(0) + env.step(0) + st_intra_prv_rt_dr_1: Router = env.game.simulation.network.get_node_by_hostname("ST-INTRA-PRV-RT-DR-1") + assert st_intra_prv_rt_dr_1.user_manager.admins["admin"].password == "red_pass" + + env.step(0) + env.step(0) + env.step(0) + env.step(0) + env.step(0) + st_intra_prv_rt_cr: Router = env.game.simulation.network.get_node_by_hostname("ST-INTRA-PRV-RT-CR") + assert st_intra_prv_rt_cr.user_manager.admins["admin"].password == "red_pass" + + env.step(0) + env.step(0) + env.step(0) + env.step(0) + env.step(0) + rem_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname("REM-PUB-RT-DR") + assert rem_pub_rt_dr.user_manager.admins["admin"].password == "red_pass" + + +def test_tap003_kill_chain_stage_exploit(): + """Tests the successful/failed handlers in the exploit stage in the InsiderKillChain""" + + env = uc7_tap003_env() + tap003: TAP003 = env.game.agents["attacker"] + # The TAP003's Target Router/Firewall + st_intra_prv_rt_dr_1: Router = env.game.simulation.network.get_node_by_hostname("ST-INTRA-PRV-RT-DR-1") + st_intra_prv_rt_cr: Router = env.game.simulation.network.get_node_by_hostname("ST-INTRA-PRV-RT-CR") + rem_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname("REM-PUB-RT-DR") + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + + env.step(0) + env.step(0) + env.step(0) + env.step(0) + env.step(0) + env.step(0) + env.step(0) + env.step(0) + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.EXPLOIT.name + + # Testing that the stage successfully impacted the simulation - Malicious ACL Added: + for _ in range(32): + env.step(0) + + # Tests that the ACL has been added and that the action is deny. + st_intra_prv_rt_dr_1_acl_list = st_intra_prv_rt_dr_1.acl + assert st_intra_prv_rt_dr_1_acl_list.acl[1].action != None + assert st_intra_prv_rt_dr_1_acl_list.acl[1].action == ACLAction.DENY + + st_intra_prv_rt_cr_acl_list = st_intra_prv_rt_cr.acl + assert st_intra_prv_rt_cr_acl_list.acl[1].action != None + assert st_intra_prv_rt_cr_acl_list.acl[1].action == ACLAction.DENY + + rem_pub_rt_dr_acl_list = rem_pub_rt_dr.acl + assert rem_pub_rt_dr_acl_list.acl[1].action != None + assert rem_pub_rt_dr_acl_list.acl[1].action == ACLAction.DENY diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_multiple_rules.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_multiple_rules.py new file mode 100644 index 00000000..036b6769 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_multiple_rules.py @@ -0,0 +1,201 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +from typing import Protocol + +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router +from primaite.utils.validation.ip_protocol import PROTOCOL_LOOKUP +from primaite.utils.validation.ipv4_address import IPV4Address +from primaite.utils.validation.port import PORT_LOOKUP + +# Defining constants. +ATTACK_AGENT_INDEX = 32 +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +REPEAT_KILL_CHAIN = False # Should the TAP repeat the kill chain after success/failure? +REPEAT_KILL_CHAIN_STAGES = False # Should the TAP restart from it's previous stage on failure? +KILL_CHAIN_PROBABILITY = 1 # Blank probability for agent 'success' +RULES = [ + { + "target_router": "ST-INTRA-PRV-RT-DR-1", + "position": 1, + "permission": "DENY", + "src_ip": "192.168.220.3", + "src_wildcard": "NONE", + "dst_ip": "192.168.220.3", + "dst_wildcard": "NONE", + "src_port": "ALL", + "dst_port": "ALL", + "protocol_name": "ALL", + }, + { + "target_router": "ST-INTRA-PRV-RT-DR-2", + "position": 5, + "permission": "DENY", + "src_ip": "192.168.220.3", + "src_wildcard": "NONE", + "dst_ip": "ALL", + "dst_wildcard": "NONE", + "src_port": "ALL", + "dst_port": "ALL", + "protocol_name": "ALL", + }, + { + "target_router": "ST-INTRA-PRV-RT-CR", + "position": 6, + "permission": "PERMIT", + "src_ip": "192.168.220.3", + "src_wildcard": "NONE", + "dst_ip": "ALL", + "dst_wildcard": "NONE", + "src_port": "ALL", + "dst_port": "ALL", + "protocol_name": "ALL", + }, + { + "target_router": "REM-PUB-RT-DR", + "position": 3, + "permission": "PERMIT", + "src_ip": "192.168.220.3", + "src_wildcard": "0.0.0.1", + "dst_ip": "192.168.220.3", + "dst_wildcard": "0.0.0.1", + "src_port": "FTP", + "dst_port": "FTP", + "protocol_name": "TCP", + }, + # +] + + +def uc7_tap003_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with the start_step & frequency set to 1 with probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain"] = kwargs["repeat_kill_chain"] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain_stages"] = kwargs[ + "repeat_kill_chain_stages" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["MANIPULATION"]["probability"] = kwargs[ + "manipulation_probability" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["ACCESS"]["probability"] = kwargs[ + "access_probability" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PLANNING"]["probability"] = kwargs[ + "planning_probability" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["EXPLOIT"]["malicious_acls"] = RULES + # Adding the new test target to TAP003's starting knowledge: + new_target_dict = { + "ST-INTRA-PRV-RT-DR-2": { + "ip_address": "192.168.170.2", + "username": "admin", + "password": "admin", + } + } + new_target_manipulation = { + "host": "ST-INTRA-PRV-RT-DR-2", + "ip_address": "192.168.170.2", + "action": "change_password", + "username": "admin", + "new_password": "red_pass", + } + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PLANNING"]["starting_network_knowledge"][ + "credentials" + ].update(new_target_dict) + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["MANIPULATION"]["account_changes"].append( + new_target_manipulation + ) + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap003_cycling_rules(): + """Tests to check that TAP003 repeats it's kill chain after success""" + + env = uc7_tap003_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + manipulation_probability=1, + access_probability=1, + planning_probability=1, + ) + tap003: TAP003 = env.game.agents["attacker"] + + def wait_until_attack(): + for _ in range(120): + # check if the agent has executed and therefore moved onto the next rule index + env.step(0) + if tap003.history[-1].action == "node-send-remote-command": + if tap003.history[-1].parameters["command"][0] == "acl": + return + pytest.fail("While testing the cycling of TAP003 rules, the agent unexpectedly didn't execute its attack.") + + wait_until_attack() + target_node: Router = env.game.simulation.network.get_node_by_hostname("ST-INTRA-PRV-RT-DR-1") + assert (rule_0 := target_node.acl.acl[1]) is not None + assert rule_0.action == ACLAction.DENY + assert rule_0.protocol == None + assert rule_0.src_ip_address == IPV4Address("192.168.220.3") + assert rule_0.src_wildcard_mask == None + assert rule_0.dst_ip_address == IPV4Address("192.168.220.3") + assert rule_0.dst_wildcard_mask == None + assert rule_0.src_port == None + assert rule_0.dst_port == None + + target_node: Router = env.game.simulation.network.get_node_by_hostname("ST-INTRA-PRV-RT-DR-2") + wait_until_attack() + assert (rule_1 := target_node.acl.acl[5]) is not None + assert rule_1.action == ACLAction.DENY + assert rule_1.protocol == None + assert rule_1.src_ip_address == IPV4Address("192.168.220.3") + assert rule_1.src_wildcard_mask == None + assert rule_1.dst_ip_address == None + assert rule_1.dst_wildcard_mask == None + assert rule_1.src_port == None + assert rule_1.dst_port == None + + wait_until_attack() + target_node: Router = env.game.simulation.network.get_node_by_hostname("ST-INTRA-PRV-RT-CR") + assert (rule_2 := target_node.acl.acl[6]) is not None + assert rule_2.action == ACLAction.PERMIT + assert rule_2.protocol == None + assert rule_2.src_ip_address == IPV4Address("192.168.220.3") + assert rule_2.src_wildcard_mask == None # default + assert rule_2.dst_ip_address == None + assert rule_2.dst_wildcard_mask == None # default + assert rule_2.src_port == None + assert rule_2.dst_port == None + + wait_until_attack() + target_node: Router = env.game.simulation.network.get_node_by_hostname("REM-PUB-RT-DR") + assert (rule_3 := target_node.acl.acl[3]) is not None + assert rule_3.action == ACLAction.PERMIT + assert rule_3.protocol == PROTOCOL_LOOKUP["TCP"] + assert rule_3.src_ip_address == IPV4Address("192.168.220.3") + assert rule_3.src_wildcard_mask == IPV4Address("0.0.0.1") + assert rule_3.dst_ip_address == IPV4Address("192.168.220.3") + assert rule_3.dst_wildcard_mask == IPV4Address("0.0.0.1") + assert rule_3.src_port == PORT_LOOKUP["FTP"] + assert rule_3.dst_port == PORT_LOOKUP["FTP"] + + # If we've gotten this fair then we can pass the test :) + pass diff --git a/tests/integration_tests/configuration_file_parsing/test_game_options_config.py b/tests/integration_tests/configuration_file_parsing/test_game_options_config.py index 4153adc0..627fc53b 100644 --- a/tests/integration_tests/configuration_file_parsing/test_game_options_config.py +++ b/tests/integration_tests/configuration_file_parsing/test_game_options_config.py @@ -8,7 +8,7 @@ from primaite.config.load import data_manipulation_config_path from primaite.game.game import PrimaiteGame from tests import TEST_ASSETS_ROOT -BASIC_CONFIG = TEST_ASSETS_ROOT / "configs/basic_switched_network.yaml" +BASIC_SWITCHED_NETWORK_CONFIG = TEST_ASSETS_ROOT / "configs/basic_switched_network.yaml" def load_config(config_path: Union[str, Path]) -> PrimaiteGame: @@ -24,3 +24,42 @@ def test_thresholds(): game = load_config(data_manipulation_config_path()) assert game.options.thresholds is not None + + +def test_nmne_threshold(): + """Test that the NMNE thresholds are properly loaded in by observation.""" + game = load_config(BASIC_SWITCHED_NETWORK_CONFIG) + + assert game.options.thresholds["nmne"] is not None + + # get NIC observation + nic_obs = game.agents["defender"].observation_manager.obs.components["NODES"].hosts[0].nics[0] + assert nic_obs.low_nmne_threshold == 5 + assert nic_obs.med_nmne_threshold == 25 + assert nic_obs.high_nmne_threshold == 100 + + +def test_file_access_threshold(): + """Test that the NMNE thresholds are properly loaded in by observation.""" + game = load_config(BASIC_SWITCHED_NETWORK_CONFIG) + + assert game.options.thresholds["file_access"] is not None + + # get file observation + file_obs = game.agents["defender"].observation_manager.obs.components["NODES"].hosts[0].folders[0].files[0] + assert file_obs.low_file_access_threshold == 2 + assert file_obs.med_file_access_threshold == 5 + assert file_obs.high_file_access_threshold == 10 + + +def test_app_executions_threshold(): + """Test that the NMNE thresholds are properly loaded in by observation.""" + game = load_config(BASIC_SWITCHED_NETWORK_CONFIG) + + assert game.options.thresholds["app_executions"] is not None + + # get application observation + app_obs = game.agents["defender"].observation_manager.obs.components["NODES"].hosts[0].applications[0] + assert app_obs.low_app_execution_threshold == 2 + assert app_obs.med_app_execution_threshold == 3 + assert app_obs.high_app_execution_threshold == 5 diff --git a/tests/integration_tests/configuration_file_parsing/test_node_file_system_config.py b/tests/integration_tests/configuration_file_parsing/test_node_file_system_config.py new file mode 100644 index 00000000..f4f96a7a --- /dev/null +++ b/tests/integration_tests/configuration_file_parsing/test_node_file_system_config.py @@ -0,0 +1,64 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +from pathlib import Path +from typing import Union + +import yaml + +from primaite.game.game import PrimaiteGame +from primaite.simulator.file_system.file_type import FileType +from tests import TEST_ASSETS_ROOT + +BASIC_CONFIG = TEST_ASSETS_ROOT / "configs/nodes_with_initial_files.yaml" + + +def load_config(config_path: Union[str, Path]) -> PrimaiteGame: + """Returns a PrimaiteGame object which loads the contents of a given yaml path.""" + with open(config_path, "r") as f: + cfg = yaml.safe_load(f) + + return PrimaiteGame.from_config(cfg) + + +def test_node_file_system_from_config(): + """Test that the appropriate files are instantiated in nodes when loaded from config.""" + game = load_config(BASIC_CONFIG) + + client_1 = game.simulation.network.get_node_by_hostname("client_1") + + assert client_1.software_manager.software.get("database-service") # database service should be installed + assert client_1.file_system.get_file(folder_name="database", file_name="database.db") # database files should exist + + assert client_1.software_manager.software.get("web-server") # web server should be installed + assert client_1.file_system.get_file(folder_name="primaite", file_name="index.html") # web files should exist + + client_2 = game.simulation.network.get_node_by_hostname("client_2") + + # database service should not be installed + assert client_2.software_manager.software.get("database-service") is None + # database files should not exist + assert client_2.file_system.get_file(folder_name="database", file_name="database.db") is None + + # web server should not be installed + assert client_2.software_manager.software.get("web-server") is None + # web files should not exist + assert client_2.file_system.get_file(folder_name="primaite", file_name="index.html") is None + + empty_folder = client_2.file_system.get_folder(folder_name="empty_folder") + assert empty_folder + assert len(empty_folder.files) == 0 # should have no files + + password_file = client_2.file_system.get_file(folder_name="root", file_name="passwords.txt") + assert password_file # should exist + assert password_file.file_type is FileType.TXT + assert password_file.size == 663 + + downloads_folder = client_2.file_system.get_folder(folder_name="downloads") + assert downloads_folder # downloads folder should exist + + test_txt = downloads_folder.get_file(file_name="test.txt") + assert test_txt # test.txt should exist + assert test_txt.file_type is FileType.TXT + + unknown_file_type = downloads_folder.get_file(file_name="another_file.pwtwoti") + assert unknown_file_type # unknown_file_type should exist + assert unknown_file_type.file_type is FileType.UNKNOWN diff --git a/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/__init__.py b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/__init__.py new file mode 100644 index 00000000..836b79af --- /dev/null +++ b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/__init__.py @@ -0,0 +1 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK diff --git a/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_kill_chain_settings.py b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_kill_chain_settings.py new file mode 100644 index 00000000..3f7f6eca --- /dev/null +++ b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_kill_chain_settings.py @@ -0,0 +1,23 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +from pathlib import Path +from typing import Union + +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.TAP003 import TAP003 +from primaite.game.game import PrimaiteGame + + +def test_tap003_kill_chain_settings_load_config(): + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["agents"][32]["agent_settings"]["kill_chain"]["MANIPULATION"]["probability"] = 0.5 + cfg["agents"][32]["agent_settings"]["kill_chain"]["ACCESS"]["probability"] = 0.5 + cfg["agents"][32]["agent_settings"]["kill_chain"]["PLANNING"]["probability"] = 0.5 + game = PrimaiteGame.from_config(cfg) + tap: TAP003 = game.agents["attacker"] + kill_chain = tap.config.agent_settings.kill_chain + assert kill_chain.MANIPULATION.probability == 0.5 + assert kill_chain.ACCESS.probability == 0.5 + assert kill_chain.PLANNING.probability == 0.5 diff --git a/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_threat_actor_profile_settings.py b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_threat_actor_profile_settings.py new file mode 100644 index 00000000..139ca154 --- /dev/null +++ b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_threat_actor_profile_settings.py @@ -0,0 +1,35 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +from pathlib import Path +from typing import Union + +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.TAP003 import TAP003 +from primaite.game.game import PrimaiteGame + + +def test_threat_actor_profile_load_config(): + """Test to check that threat actor profiles are able to be loaded.""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + + game = PrimaiteGame.from_config(cfg) + # tap003 is found and loaded TODO: Once tuple digestion is implemented, change to hardcoded 'tap003' test. + assert "attacker" in game.agents + assert isinstance(game.agents["attacker"], TAP003) + agent: TAP003 = game.agents["attacker"] + assert agent.config.agent_settings.start_step == 1 + assert agent.config.agent_settings.frequency == 3 + assert agent.config.agent_settings.variance == 0 + assert not agent.config.agent_settings.repeat_kill_chain + assert agent.config.agent_settings.repeat_kill_chain_stages + assert agent.config.agent_settings.default_starting_node == "ST-PROJ-A-PRV-PC-1" + assert not agent.config.agent_settings.starting_nodes + assert agent.config.agent_settings.kill_chain.PLANNING.probability == 1 + assert len(agent.config.agent_settings.kill_chain.PLANNING.starting_network_knowledge["credentials"]) == 6 + assert agent.config.agent_settings.kill_chain.ACCESS.probability == 1 + assert agent.config.agent_settings.kill_chain.MANIPULATION.probability == 1 + assert len(agent.config.agent_settings.kill_chain.MANIPULATION.account_changes) == 3 + assert agent.config.agent_settings.kill_chain.EXPLOIT.probability == 1 + assert len(agent.config.agent_settings.kill_chain.EXPLOIT.malicious_acls) == 3 diff --git a/tests/integration_tests/extensions/nodes/giga_switch.py b/tests/integration_tests/extensions/nodes/giga_switch.py index d9599618..5c202ed2 100644 --- a/tests/integration_tests/extensions/nodes/giga_switch.py +++ b/tests/integration_tests/extensions/nodes/giga_switch.py @@ -49,7 +49,7 @@ class GigaSwitch(NetworkNode, discriminator="gigaswitch"): if markdown: table.set_style(MARKDOWN) table.align = "l" - table.title = f"{self.hostname} Switch Ports" + table.title = f"{self.config.hostname} Switch Ports" for port_num, port in self.network_interface.items(): table.add_row([port_num, port.mac_address, port.speed, "Enabled" if port.enabled else "Disabled"]) print(table) diff --git a/tests/integration_tests/game_layer/actions/test_terminal_actions.py b/tests/integration_tests/game_layer/actions/test_terminal_actions.py index c39d8263..3ee97fb7 100644 --- a/tests/integration_tests/game_layer/actions/test_terminal_actions.py +++ b/tests/integration_tests/game_layer/actions/test_terminal_actions.py @@ -106,7 +106,6 @@ def test_remote_login_change_password(game_and_agent_fixture: Tuple[PrimaiteGame "username": "user123", "current_password": "password", "new_password": "different_password", - "remote_ip": str(server_1.network_interface[1].ip_address), }, ) agent.store_action(action) @@ -146,7 +145,6 @@ def test_change_password_logs_out_user(game_and_agent_fixture: Tuple[PrimaiteGam "username": "user123", "current_password": "password", "new_password": "different_password", - "remote_ip": str(server_1.network_interface[1].ip_address), }, ) agent.store_action(action) @@ -166,3 +164,55 @@ def test_change_password_logs_out_user(game_and_agent_fixture: Tuple[PrimaiteGam assert server_1.file_system.get_folder("folder123") is None assert server_1.file_system.get_file("folder123", "doggo.pdf") is None + + +def test_local_terminal(game_and_agent_fixture: Tuple[PrimaiteGame, ProxyAgent]): + game, agent = game_and_agent_fixture + + client_1 = game.simulation.network.get_node_by_hostname("client_1") + # create a new user account on server_1 that will be logged into remotely + client_1_usm: UserManager = client_1.software_manager.software["user-manager"] + client_1_usm.add_user("user123", "password", is_admin=True) + + action = ( + "node-send-local-command", + { + "node_name": "client_1", + "username": "user123", + "password": "password", + "command": ["file_system", "create", "file", "folder123", "doggo.pdf", False], + }, + ) + agent.store_action(action) + game.step() + + assert client_1.file_system.get_folder("folder123") + assert client_1.file_system.get_file("folder123", "doggo.pdf") + + # Change password + action = ( + "node-account-change-password", + { + "node_name": "client_1", + "username": "user123", + "current_password": "password", + "new_password": "different_password", + }, + ) + agent.store_action(action) + game.step() + + action = ( + "node-send-local-command", + { + "node_name": "client_1", + "username": "user123", + "password": "password", + "command": ["file_system", "create", "file", "folder123", "cat.pdf", False], + }, + ) + agent.store_action(action) + game.step() + + assert client_1.file_system.get_file("folder123", "cat.pdf") is None + client_1.session_manager.show() diff --git a/tests/integration_tests/game_layer/actions/test_user_account_actions.py b/tests/integration_tests/game_layer/actions/test_user_account_actions.py new file mode 100644 index 00000000..340a093a --- /dev/null +++ b/tests/integration_tests/game_layer/actions/test_user_account_actions.py @@ -0,0 +1,176 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest + +from primaite.simulator.network.hardware.nodes.host.computer import Computer +from primaite.simulator.network.hardware.nodes.network.router import ACLAction +from primaite.utils.validation.port import Port, PORT_LOOKUP + + +@pytest.fixture +def game_and_agent_fixture(game_and_agent): + """Create a game with a simple agent that can be controlled by the tests.""" + game, agent = game_and_agent + + client_1: Computer = game.simulation.network.get_node_by_hostname("client_1") + client_1.start_up_duration = 3 + + return (game, agent) + + +def test_user_account_add_user_action(game_and_agent_fixture): + """Tests the add user account action.""" + game, agent = game_and_agent_fixture + client_1 = game.simulation.network.get_node_by_hostname("client_1") + + assert len(client_1.user_manager.users) == 1 # admin is created by default + assert len(client_1.user_manager.admins) == 1 + + # add admin account + action = ( + "node-account-add-user", + {"node_name": "client_1", "username": "admin_2", "password": "e-tronic-boogaloo", "is_admin": True}, + ) + agent.store_action(action) + game.step() + + assert len(client_1.user_manager.users) == 2 # new user added + assert len(client_1.user_manager.admins) == 2 + + # add non admin account + action = ( + "node-account-add-user", + {"node_name": "client_1", "username": "leeroy.jenkins", "password": "no_plan_needed", "is_admin": False}, + ) + agent.store_action(action) + game.step() + + assert len(client_1.user_manager.users) == 3 # new user added + assert len(client_1.user_manager.admins) == 2 + + +def test_user_account_disable_user_action(game_and_agent_fixture): + """Tests the disable user account action.""" + game, agent = game_and_agent_fixture + client_1 = game.simulation.network.get_node_by_hostname("client_1") + + client_1.user_manager.add_user(username="test", password="password", is_admin=True) + assert len(client_1.user_manager.users) == 2 # new user added + assert len(client_1.user_manager.admins) == 2 + + test_user = client_1.user_manager.users.get("test") + assert test_user + assert test_user.disabled is not True + + # disable test account + action = ( + "node-account-disable-user", + { + "node_name": "client_1", + "username": "test", + }, + ) + agent.store_action(action) + game.step() + assert test_user.disabled + + +def test_user_account_change_password_action(game_and_agent_fixture): + """Tests the change password user account action.""" + game, agent = game_and_agent_fixture + client_1 = game.simulation.network.get_node_by_hostname("client_1") + + client_1.user_manager.add_user(username="test", password="password", is_admin=True) + + test_user = client_1.user_manager.users.get("test") + assert test_user.password == "password" + + # change account password + action = ( + "node-account-change-password", + {"node_name": "client_1", "username": "test", "current_password": "password", "new_password": "2Hard_2_Hack"}, + ) + agent.store_action(action) + game.step() + + assert test_user.password == "2Hard_2_Hack" + + +def test_user_account_create_terminal_action(game_and_agent_fixture): + """Tests that agents can use the terminal to create new users.""" + game, agent = game_and_agent_fixture + + router = game.simulation.network.get_node_by_hostname("router") + router.acl.add_rule(action=ACLAction.PERMIT, src_port=PORT_LOOKUP["SSH"], dst_port=PORT_LOOKUP["SSH"], position=4) + + server_1 = game.simulation.network.get_node_by_hostname("server_1") + server_1_usm = server_1.software_manager.software["user-manager"] + server_1_usm.add_user("user123", "password", is_admin=True) + + action = ( + "node-session-remote-login", + { + "node_name": "client_1", + "username": "user123", + "password": "password", + "remote_ip": str(server_1.network_interface[1].ip_address), + }, + ) + agent.store_action(action) + game.step() + assert agent.history[-1].response.status == "success" + + # Create a new user account via terminal. + action = ( + "node-send-remote-command", + { + "node_name": "client_1", + "remote_ip": str(server_1.network_interface[1].ip_address), + "command": ["service", "user-manager", "add_user", "new_user", "new_pass", True], + }, + ) + agent.store_action(action) + game.step() + new_user = server_1.user_manager.users.get("new_user") + assert new_user + assert new_user.password == "new_pass" + assert new_user.disabled is not True + + +def test_user_account_disable_terminal_action(game_and_agent_fixture): + """Tests that agents can use the terminal to disable users.""" + game, agent = game_and_agent_fixture + router = game.simulation.network.get_node_by_hostname("router") + router.acl.add_rule(action=ACLAction.PERMIT, src_port=PORT_LOOKUP["SSH"], dst_port=PORT_LOOKUP["SSH"], position=4) + + server_1 = game.simulation.network.get_node_by_hostname("server_1") + server_1_usm = server_1.software_manager.software["user-manager"] + server_1_usm.add_user("user123", "password", is_admin=True) + + action = ( + "node-session-remote-login", + { + "node_name": "client_1", + "username": "user123", + "password": "password", + "remote_ip": str(server_1.network_interface[1].ip_address), + }, + ) + agent.store_action(action) + game.step() + assert agent.history[-1].response.status == "success" + + # Disable a user via terminal + action = ( + "node-send-remote-command", + { + "node_name": "client_1", + "remote_ip": str(server_1.network_interface[1].ip_address), + "command": ["service", "user-manager", "disable_user", "user123"], + }, + ) + agent.store_action(action) + game.step() + + new_user = server_1.user_manager.users.get("user123") + assert new_user + assert new_user.disabled is True diff --git a/tests/integration_tests/game_layer/observations/test_file_system_observations.py b/tests/integration_tests/game_layer/observations/test_file_system_observations.py index 7323461c..722fd294 100644 --- a/tests/integration_tests/game_layer/observations/test_file_system_observations.py +++ b/tests/integration_tests/game_layer/observations/test_file_system_observations.py @@ -44,6 +44,38 @@ def test_file_observation(simulation): assert observation_state.get("health_status") == 3 # corrupted +def test_config_file_access_categories(simulation): + pc: Computer = simulation.network.get_node_by_hostname("client_1") + file_obs = FileObservation( + where=["network", "nodes", pc.config.hostname, "file_system", "folders", "root", "files", "dog.png"], + include_num_access=False, + file_system_requires_scan=True, + thresholds={"file_access": {"low": 3, "medium": 6, "high": 9}}, + ) + + assert file_obs.high_file_access_threshold == 9 + assert file_obs.med_file_access_threshold == 6 + assert file_obs.low_file_access_threshold == 3 + + with pytest.raises(Exception): + # should throw an error + FileObservation( + where=["network", "nodes", pc.config.hostname, "file_system", "folders", "root", "files", "dog.png"], + include_num_access=False, + file_system_requires_scan=True, + thresholds={"file_access": {"low": 9, "medium": 6, "high": 9}}, + ) + + with pytest.raises(Exception): + # should throw an error + FileObservation( + where=["network", "nodes", pc.config.hostname, "file_system", "folders", "root", "files", "dog.png"], + include_num_access=False, + file_system_requires_scan=True, + thresholds={"file_access": {"low": 3, "medium": 9, "high": 9}}, + ) + + def test_folder_observation(simulation): """Test the folder observation.""" pc: Computer = simulation.network.get_node_by_hostname("client_1") diff --git a/tests/integration_tests/game_layer/observations/test_nic_observations.py b/tests/integration_tests/game_layer/observations/test_nic_observations.py index b5e5ca81..30eccb06 100644 --- a/tests/integration_tests/game_layer/observations/test_nic_observations.py +++ b/tests/integration_tests/game_layer/observations/test_nic_observations.py @@ -77,6 +77,14 @@ def test_nic(simulation): nic_obs = NICObservation(where=["network", "nodes", pc.config.hostname, "NICs", 1], include_nmne=True) + # The Simulation object created by the fixture also creates the + # NICObservation class with the NICObservation.capture_nmnme class variable + # set to False. Under normal (non-test) circumstances this class variable + # is set from a config file such as data_manipulation.yaml. So although + # capture_nmne is set to True in the NetworkInterface class it's still False + # in the NICObservation class so we set it now. + nic_obs.capture_nmne = True + # Set the NMNE configuration to capture DELETE/ENCRYPT queries as MNEs nmne_config = { "capture_nmne": True, # Enable the capture of MNEs @@ -115,14 +123,11 @@ def test_nic_categories(simulation): assert nic_obs.low_nmne_threshold == 0 # default -@pytest.mark.skip(reason="Feature not implemented yet") def test_config_nic_categories(simulation): pc: Computer = simulation.network.get_node_by_hostname("client_1") nic_obs = NICObservation( - where=["network", "nodes", pc.hostname, "NICs", 1], - low_nmne_threshold=3, - med_nmne_threshold=6, - high_nmne_threshold=9, + where=["network", "nodes", pc.config.hostname, "NICs", 1], + thresholds={"nmne": {"low": 3, "medium": 6, "high": 9}}, include_nmne=True, ) @@ -133,20 +138,16 @@ def test_config_nic_categories(simulation): with pytest.raises(Exception): # should throw an error NICObservation( - where=["network", "nodes", pc.hostname, "NICs", 1], - low_nmne_threshold=9, - med_nmne_threshold=6, - high_nmne_threshold=9, + where=["network", "nodes", pc.config.hostname, "NICs", 1], + thresholds={"nmne": {"low": 9, "medium": 6, "high": 9}}, include_nmne=True, ) with pytest.raises(Exception): # should throw an error NICObservation( - where=["network", "nodes", pc.hostname, "NICs", 1], - low_nmne_threshold=3, - med_nmne_threshold=9, - high_nmne_threshold=9, + where=["network", "nodes", pc.config.hostname, "NICs", 1], + thresholds={"nmne": {"low": 3, "medium": 9, "high": 9}}, include_nmne=True, ) diff --git a/tests/integration_tests/game_layer/observations/test_node_observations.py b/tests/integration_tests/game_layer/observations/test_node_observations.py index 09eb3fe4..aef60bc2 100644 --- a/tests/integration_tests/game_layer/observations/test_node_observations.py +++ b/tests/integration_tests/game_layer/observations/test_node_observations.py @@ -39,6 +39,8 @@ def test_host_observation(simulation): folders=[], network_interfaces=[], file_system_requires_scan=True, + services_requires_scan=True, + applications_requires_scan=True, include_users=False, ) diff --git a/tests/integration_tests/game_layer/observations/test_obs_data_capture.py b/tests/integration_tests/game_layer/observations/test_obs_data_capture.py new file mode 100644 index 00000000..f0143281 --- /dev/null +++ b/tests/integration_tests/game_layer/observations/test_obs_data_capture.py @@ -0,0 +1,28 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import json + +from primaite.session.environment import PrimaiteGymEnv +from primaite.session.io import PrimaiteIO +from tests import TEST_ASSETS_ROOT + +DATA_MANIPULATION_CONFIG = TEST_ASSETS_ROOT / "configs" / "data_manipulation.yaml" + + +def test_obs_data_in_log_file(): + """Create a log file of AgentHistoryItems and check observation data is + included. Assumes that data_manipulation.yaml has an agent labelled + 'defender' with a non-null observation space. + The log file will be in: + primaite/VERSION/sessions/YYYY-MM-DD/HH-MM-SS/agent_actions + """ + env = PrimaiteGymEnv(DATA_MANIPULATION_CONFIG) + env.reset() + for _ in range(10): + env.step(0) + env.reset() + io = PrimaiteIO() + path = io.generate_agent_actions_save_path(episode=1) + with open(path, "r") as f: + j = json.load(f) + + assert type(j["0"]["defender"]["observation"]) == dict diff --git a/tests/integration_tests/game_layer/observations/test_software_observations.py b/tests/integration_tests/game_layer/observations/test_software_observations.py index 28cdaf01..1ebff10c 100644 --- a/tests/integration_tests/game_layer/observations/test_software_observations.py +++ b/tests/integration_tests/game_layer/observations/test_software_observations.py @@ -29,7 +29,9 @@ def test_service_observation(simulation): ntp_server = pc.software_manager.software.get("ntp-server") assert ntp_server - service_obs = ServiceObservation(where=["network", "nodes", pc.config.hostname, "services", "ntp-server"]) + service_obs = ServiceObservation( + where=["network", "nodes", pc.config.hostname, "services", "ntp-server"], services_requires_scan=True + ) assert service_obs.space["operating_status"] == spaces.Discrete(7) assert service_obs.space["health_status"] == spaces.Discrete(5) @@ -54,7 +56,9 @@ def test_application_observation(simulation): web_browser: WebBrowser = pc.software_manager.software.get("web-browser") assert web_browser - app_obs = ApplicationObservation(where=["network", "nodes", pc.config.hostname, "applications", "web-browser"]) + app_obs = ApplicationObservation( + where=["network", "nodes", pc.config.hostname, "applications", "web-browser"], applications_requires_scan=True + ) web_browser.close() observation_state = app_obs.observe(simulation.describe_state()) @@ -69,3 +73,33 @@ def test_application_observation(simulation): assert observation_state.get("health_status") == 1 assert observation_state.get("operating_status") == 1 # running assert observation_state.get("num_executions") == 1 + + +def test_application_executions_categories(simulation): + pc: Computer = simulation.network.get_node_by_hostname("client_1") + + app_obs = ApplicationObservation( + where=["network", "nodes", pc.config.hostname, "applications", "WebBrowser"], + applications_requires_scan=False, + thresholds={"app_executions": {"low": 3, "medium": 6, "high": 9}}, + ) + + assert app_obs.high_app_execution_threshold == 9 + assert app_obs.med_app_execution_threshold == 6 + assert app_obs.low_app_execution_threshold == 3 + + with pytest.raises(Exception): + # should throw an error + ApplicationObservation( + where=["network", "nodes", pc.config.hostname, "applications", "WebBrowser"], + applications_requires_scan=False, + thresholds={"app_executions": {"low": 9, "medium": 6, "high": 9}}, + ) + + with pytest.raises(Exception): + # should throw an error + ApplicationObservation( + where=["network", "nodes", pc.config.hostname, "applications", "WebBrowser"], + applications_requires_scan=False, + thresholds={"app_executions": {"low": 3, "medium": 9, "high": 9}}, + ) diff --git a/tests/integration_tests/game_layer/test_RNG_seed.py b/tests/integration_tests/game_layer/test_RNG_seed.py index 45fa445d..2b80e153 100644 --- a/tests/integration_tests/game_layer/test_RNG_seed.py +++ b/tests/integration_tests/game_layer/test_RNG_seed.py @@ -7,6 +7,7 @@ import yaml from primaite.config.load import data_manipulation_config_path from primaite.game.agent.interface import AgentHistoryItem from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator import SIM_OUTPUT @pytest.fixture() @@ -33,6 +34,11 @@ def test_rng_seed_set(create_env): assert a == b + # Check that seed log file was created. + path = SIM_OUTPUT.path / "seed.log" + with open(path, "r") as file: + assert file + def test_rng_seed_unset(create_env): """Test with no RNG seed.""" @@ -48,3 +54,19 @@ def test_rng_seed_unset(create_env): b = [item.timestep for item in env.game.agents["client_2_green_user"].history if item.action != "do-nothing"] assert a != b + + +def test_for_generated_seed(): + """ + Show that setting generate_seed_value to true producess a valid seed. + """ + with open(data_manipulation_config_path(), "r") as f: + cfg = yaml.safe_load(f) + + cfg["game"]["generate_seed_value"] = True + PrimaiteGymEnv(env_config=cfg) + path = SIM_OUTPUT.path / "seed.log" + with open(path, "r") as file: + data = file.read() + + assert data.split(" ")[3] != None diff --git a/tests/integration_tests/game_layer/test_actions.py b/tests/integration_tests/game_layer/test_actions.py index 03b94ab7..59bee385 100644 --- a/tests/integration_tests/game_layer/test_actions.py +++ b/tests/integration_tests/game_layer/test_actions.py @@ -22,6 +22,7 @@ from primaite.game.game import PrimaiteGame from primaite.session.environment import PrimaiteGymEnv from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus from primaite.simulator.network.hardware.nodes.network.firewall import Firewall +from primaite.simulator.network.hardware.nodes.network.router import Router from primaite.simulator.system.applications.application import ApplicationOperatingState from primaite.simulator.system.applications.web_browser import WebBrowser from primaite.simulator.system.software import SoftwareHealthState @@ -107,7 +108,7 @@ def test_router_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, Prox """ Test that the RouterACLAddRuleAction can form a request and that it is accepted by the simulation. - The acl starts off with 4 rules, and we add a rule, and check that the acl now has 5 rules. + The ACL starts off with 4 rules, and we add a rule, and check that the ACL now has 5 rules. """ game, agent = game_and_agent @@ -164,11 +165,9 @@ def test_router_acl_addrule_integration(game_and_agent: Tuple[PrimaiteGame, Prox }, ) agent.store_action(action) - print(agent.most_recent_action) game.step() - print(agent.most_recent_action) + # 5: Check that the ACL now has 6 rules, but that server_1 can still ping server_2 - print(router.acl.show()) assert router.acl.num_rules == 6 assert server_1.ping("10.0.2.3") # Can ping server_2 @@ -180,7 +179,8 @@ def test_router_acl_removerule_integration(game_and_agent: Tuple[PrimaiteGame, P # 1: Check that http traffic is going across the network nicely. client_1 = game.simulation.network.get_node_by_hostname("client_1") server_1 = game.simulation.network.get_node_by_hostname("server_1") - router = game.simulation.network.get_node_by_hostname("router") + router: Router = game.simulation.network.get_node_by_hostname("router") + assert router.acl.num_rules == 4 browser: WebBrowser = client_1.software_manager.software.get("web-browser") browser.run() diff --git a/tests/integration_tests/network/test_capture_nmne.py b/tests/integration_tests/network/test_capture_nmne.py index ea7fbc99..80e7c3b3 100644 --- a/tests/integration_tests/network/test_capture_nmne.py +++ b/tests/integration_tests/network/test_capture_nmne.py @@ -1,5 +1,11 @@ # © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +from itertools import product + +import yaml + +from primaite.config.load import data_manipulation_config_path from primaite.game.agent.observations.nic_observations import NICObservation +from primaite.session.environment import PrimaiteGymEnv from primaite.simulator.network.container import Network from primaite.simulator.network.hardware.nodes.host.host_node import NIC from primaite.simulator.network.hardware.nodes.host.server import Server @@ -277,3 +283,19 @@ def test_capture_nmne_observations(uc2_network: Network): assert web_nic_obs["outbound"] == expected_nmne assert db_nic_obs["inbound"] == expected_nmne uc2_network.apply_timestep(timestep=0) + + +def test_nmne_parameter_settings(): + """ + Check that the four permutations of the values of capture_nmne and + include_nmne work as expected. + """ + + with open(data_manipulation_config_path(), "r") as f: + cfg = yaml.safe_load(f) + + DEFENDER = 3 + for capture, include in product([True, False], [True, False]): + cfg["simulation"]["network"]["nmne_config"]["capture_nmne"] = capture + cfg["agents"][DEFENDER]["observation_space"]["options"]["components"][0]["options"]["include_nmne"] = include + PrimaiteGymEnv(env_config=cfg) diff --git a/tests/integration_tests/system/test_arp.py b/tests/integration_tests/system/test_arp.py index 055d58c6..85fa3abd 100644 --- a/tests/integration_tests/system/test_arp.py +++ b/tests/integration_tests/system/test_arp.py @@ -1,6 +1,7 @@ # © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK -from primaite.simulator.network.hardware.nodes.network.router import RouterARP +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router, RouterARP from primaite.simulator.system.services.arp.arp import ARP +from primaite.utils.validation.port import PORT_LOOKUP from tests.integration_tests.network.test_routing import multi_hop_network @@ -48,3 +49,19 @@ def test_arp_fails_for_network_address_between_routers(multi_hop_network): actual_result = router_1_arp.get_arp_cache_mac_address(router_1.network_interface[1].ip_network.network_address) assert actual_result == expected_result + + +def test_arp_not_affected_by_acl(multi_hop_network): + pc_a = multi_hop_network.get_node_by_hostname("pc_a") + router_1: Router = multi_hop_network.get_node_by_hostname("router_1") + + # Add explicit rule to block ARP traffic. This shouldn't actually stop ARP traffic + # as it operates a different layer within the network. + router_1.acl.add_rule(action=ACLAction.DENY, src_port=PORT_LOOKUP["ARP"], dst_port=PORT_LOOKUP["ARP"], position=23) + + pc_a_arp: ARP = pc_a.software_manager.arp + + expected_result = router_1.network_interface[2].mac_address + actual_result = pc_a_arp.get_arp_cache_mac_address(router_1.network_interface[2].ip_address) + + assert actual_result == expected_result diff --git a/tests/unit_tests/_primaite/_game/_agent/test_observations.py b/tests/unit_tests/_primaite/_game/_agent/test_observations.py index 5d5921a9..3df6ca0a 100644 --- a/tests/unit_tests/_primaite/_game/_agent/test_observations.py +++ b/tests/unit_tests/_primaite/_game/_agent/test_observations.py @@ -1,10 +1,11 @@ # © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import json from typing import List import pytest import yaml -from primaite.game.agent.observations import ObservationManager +from primaite.game.agent.observations import ApplicationObservation, ObservationManager, ServiceObservation from primaite.game.agent.observations.file_system_observations import FileObservation, FolderObservation from primaite.game.agent.observations.host_observations import HostObservation @@ -136,3 +137,227 @@ class TestFileSystemRequiresScan: [], files=[], num_files=0, include_num_access=False, file_system_requires_scan=True ) assert obs_requiring_scan.observe(folder_state)["health_status"] == 1 + + +class TestServicesRequiresScan: + @pytest.mark.parametrize( + ("yaml_option_string", "expected_val"), + ( + ("services_requires_scan: true", True), + ("services_requires_scan: false", False), + (" ", True), + ), + ) + def test_obs_config(self, yaml_option_string, expected_val): + """Check that the default behaviour is to set service_requires_scan to True.""" + obs_cfg_yaml = f""" + type: custom + options: + components: + - type: nodes + label: NODES + options: + hosts: + - hostname: domain_controller + - hostname: web_server + services: + - service_name: web-server + - service_name: dns-client + - hostname: database_server + folders: + - folder_name: database + files: + - file_name: database.db + - hostname: backup_server + services: + - service_name: ftp-server + - hostname: security_suite + - hostname: client_1 + - hostname: client_2 + num_services: 3 + num_applications: 0 + num_folders: 1 + num_files: 1 + num_nics: 2 + include_num_access: false + {yaml_option_string} + include_nmne: true + monitored_traffic: + icmp: + - NONE + tcp: + - DNS + routers: + - hostname: router_1 + num_ports: 0 + ip_list: + - 192.168.1.10 + - 192.168.1.12 + - 192.168.1.14 + - 192.168.1.16 + - 192.168.1.110 + - 192.168.10.21 + - 192.168.10.22 + - 192.168.10.110 + wildcard_list: + - 0.0.0.1 + port_list: + - 80 + - 5432 + protocol_list: + - ICMP + - TCP + - UDP + num_rules: 10 + + - type: links + label: LINKS + options: + link_references: + - router_1:eth-1<->switch_1:eth-8 + - router_1:eth-2<->switch_2:eth-8 + - switch_1:eth-1<->domain_controller:eth-1 + - switch_1:eth-2<->web_server:eth-1 + - switch_1:eth-3<->database_server:eth-1 + - switch_1:eth-4<->backup_server:eth-1 + - switch_1:eth-7<->security_suite:eth-1 + - switch_2:eth-1<->client_1:eth-1 + - switch_2:eth-2<->client_2:eth-1 + - switch_2:eth-7<->security_suite:eth-2 + - type: none + label: ICS + options: {{}} + + """ + + cfg = yaml.safe_load(obs_cfg_yaml) + manager = ObservationManager.from_config(cfg) + + hosts: List[HostObservation] = manager.obs.components["NODES"].hosts + for i, host in enumerate(hosts): + services: List[ServiceObservation] = host.services + for j, service in enumerate(services): + val = service.services_requires_scan + print(f"host {i} service {j} {val}") + assert val == expected_val # Make sure services require scan by default + + def test_services_requires_scan(self): + state = {"health_state_actual": 3, "health_state_visible": 1, "operating_state": 1} + + obs_requiring_scan = ServiceObservation([], services_requires_scan=True) + assert obs_requiring_scan.observe(state)["health_status"] == 1 # should be visible value + + obs_not_requiring_scan = ServiceObservation([], services_requires_scan=False) + assert obs_not_requiring_scan.observe(state)["health_status"] == 3 # should be actual value + + +class TestApplicationsRequiresScan: + @pytest.mark.parametrize( + ("yaml_option_string", "expected_val"), + ( + ("applications_requires_scan: true", True), + ("applications_requires_scan: false", False), + (" ", True), + ), + ) + def test_obs_config(self, yaml_option_string, expected_val): + """Check that the default behaviour is to set applications_requires_scan to True.""" + obs_cfg_yaml = f""" + type: custom + options: + components: + - type: nodes + label: NODES + options: + hosts: + - hostname: domain_controller + - hostname: web_server + - hostname: database_server + folders: + - folder_name: database + files: + - file_name: database.db + - hostname: backup_server + - hostname: security_suite + - hostname: client_1 + applications: + - application_name: web-browser + - hostname: client_2 + applications: + - application_name: web-browser + - application_name: database-client + num_services: 0 + num_applications: 3 + num_folders: 1 + num_files: 1 + num_nics: 2 + include_num_access: false + {yaml_option_string} + include_nmne: true + monitored_traffic: + icmp: + - NONE + tcp: + - DNS + routers: + - hostname: router_1 + num_ports: 0 + ip_list: + - 192.168.1.10 + - 192.168.1.12 + - 192.168.1.14 + - 192.168.1.16 + - 192.168.1.110 + - 192.168.10.21 + - 192.168.10.22 + - 192.168.10.110 + wildcard_list: + - 0.0.0.1 + port_list: + - 80 + - 5432 + protocol_list: + - ICMP + - TCP + - UDP + num_rules: 10 + + - type: links + label: LINKS + options: + link_references: + - router_1:eth-1<->switch_1:eth-8 + - router_1:eth-2<->switch_2:eth-8 + - switch_1:eth-1<->domain_controller:eth-1 + - switch_1:eth-2<->web_server:eth-1 + - switch_1:eth-3<->database_server:eth-1 + - switch_1:eth-4<->backup_server:eth-1 + - switch_1:eth-7<->security_suite:eth-1 + - switch_2:eth-1<->client_1:eth-1 + - switch_2:eth-2<->client_2:eth-1 + - switch_2:eth-7<->security_suite:eth-2 + - type: none + label: ICS + options: {{}} + + """ + + cfg = yaml.safe_load(obs_cfg_yaml) + manager = ObservationManager.from_config(cfg) + + hosts: List[HostObservation] = manager.obs.components["NODES"].hosts + for i, host in enumerate(hosts): + services: List[ServiceObservation] = host.services + for j, service in enumerate(services): + val = service.services_requires_scan + print(f"host {i} service {j} {val}") + assert val == expected_val # Make sure applications require scan by default + + def test_applications_requires_scan(self): + state = {"health_state_actual": 3, "health_state_visible": 1, "operating_state": 1, "num_executions": 1} + + obs_requiring_scan = ApplicationObservation([], applications_requires_scan=True) + assert obs_requiring_scan.observe(state)["health_status"] == 1 # should be visible value + + obs_not_requiring_scan = ApplicationObservation([], applications_requires_scan=False) + assert obs_not_requiring_scan.observe(state)["health_status"] == 3 # should be actual value diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp_client.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp_client.py index 91369f6c..81e05467 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp_client.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp_client.py @@ -73,7 +73,7 @@ def test_ftp_should_not_process_commands_if_service_not_running(ftp_client): assert ftp_client_service._process_ftp_command(payload=payload).status_code is FTPStatusCode.ERROR -def test_ftp_tries_to_senf_file__that_does_not_exist(ftp_client): +def test_ftp_tries_to_send_file__that_does_not_exist(ftp_client): """Method send_file should return false if no file to send.""" assert ftp_client.file_system.get_file(folder_name="root", file_name="test.txt") is None diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_terminal.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_terminal.py index 32fcae9a..3b2377e9 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_terminal.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_terminal.py @@ -6,6 +6,7 @@ import pytest from primaite.game.agent.interface import ProxyAgent from primaite.game.game import PrimaiteGame +from primaite.interface.request import RequestResponse from primaite.simulator.network.container import Network from primaite.simulator.network.hardware.nodes.host.computer import Computer from primaite.simulator.network.hardware.nodes.host.server import Server @@ -442,3 +443,59 @@ def test_terminal_connection_timeout(basic_network): assert len(computer_b.user_session_manager.remote_sessions) == 0 assert not remote_connection.is_active + + +def test_terminal_last_response_updates(basic_network): + """Test that the _last_response within Terminal correctly updates.""" + network: Network = basic_network + computer_a: Computer = network.get_node_by_hostname("node_a") + terminal_a: Terminal = computer_a.software_manager.software.get("terminal") + computer_b: Computer = network.get_node_by_hostname("node_b") + + assert terminal_a.last_response is None + + remote_connection = terminal_a.login(username="admin", password="admin", ip_address="192.168.0.11") + + # Last response should be a successful logon + assert terminal_a.last_response == RequestResponse(status="success", data={"reason": "Login Successful"}) + + remote_connection.execute(command=["software_manager", "application", "install", "ransomware-script"]) + + # Last response should now update following successful install + assert terminal_a.last_response == RequestResponse(status="success", data={}) + + remote_connection.execute(command=["software_manager", "application", "install", "ransomware-script"]) + + # Last response should now update to success, but with supplied reason. + assert terminal_a.last_response == RequestResponse(status="success", data={"reason": "already installed"}) + + remote_connection.execute(command=["file_system", "create", "file", "folder123", "doggo.pdf", False]) + + # Check file was created. + assert computer_b.file_system.access_file(folder_name="folder123", file_name="doggo.pdf") + + # Last response should be confirmation of file creation. + assert terminal_a.last_response == RequestResponse( + status="success", + data={"file_name": "doggo.pdf", "folder_name": "folder123", "file_type": "PDF", "file_size": 102400}, + ) + + remote_connection.execute( + command=[ + "service", + "ftp-client", + "send", + { + "dest_ip_address": "192.168.0.2", + "src_folder": "folder123", + "src_file_name": "cat.pdf", + "dest_folder": "root", + "dest_file_name": "cat.pdf", + }, + ] + ) + + assert terminal_a.last_response == RequestResponse( + status="failure", + data={"reason": "Unable to locate given file on local file system. Perhaps given options are invalid?"}, + )