diff --git a/docs/source/simulation_components/system/applications/c2_suite.rst b/docs/source/simulation_components/system/applications/c2_suite.rst index ab6a49e2..28bb1bf8 100644 --- a/docs/source/simulation_components/system/applications/c2_suite.rst +++ b/docs/source/simulation_components/system/applications/c2_suite.rst @@ -13,7 +13,7 @@ malicious network architecture and begin to further the realism of red agents wi Overview: ========= -These two new classes give red agents a cyber realistic way of leveraging the capabilities of the ``Terminal`` application whilst introducing more opportunities for the blue agent to notice and subvert a red agent during an episode. +These two new classes give red agents a cyber realistic way of leveraging the capabilities of the ``Terminal`` application whilst introducing more opportunities for the blue agent(s) to notice and subvert a red agent during an episode. For a more in-depth look at the command and control applications then please refer to the ``C2-E2E-Notebook``. diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index d3035a5a..045b2467 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -461,15 +461,9 @@ class PrimaiteGame: opt = application_cfg["options"] new_application.configure( c2_server_ip_address=IPv4Address(opt.get("c2_server_ip_address")), - keep_alive_frequency=(opt.get("keep_alive_frequency", 5)) - if opt.get("keep_alive_frequency") - else 5, - masquerade_protocol=IPProtocol[(opt.get("masquerade_protocol"))] - if opt.get("masquerade_protocol") - else IPProtocol.TCP, - masquerade_port=Port[(opt.get("masquerade_port"))] - if opt.get("masquerade_port") - else Port.HTTP, + keep_alive_frequency=(opt.get("keep_alive_frequency", 5)), + masquerade_protocol=IPProtocol[(opt.get("masquerade_protocol", IPProtocol.TCP))], + masquerade_port=Port[(opt.get("masquerade_port", Port.HTTP))], ) if "network_interfaces" in node_cfg: for nic_num, nic_cfg in node_cfg["network_interfaces"].items(): diff --git a/src/primaite/notebooks/Command-&-Control-E2E-Demonstration.ipynb b/src/primaite/notebooks/Command-&-Control-E2E-Demonstration.ipynb index 9da39e32..03e50ae4 100644 --- a/src/primaite/notebooks/Command-&-Control-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Command-&-Control-E2E-Demonstration.ipynb @@ -191,7 +191,7 @@ "Before the Red Agent is able to perform any C2 specific actions, the C2 Server needs to be installed and run before the Red Agent can perform any C2 specific action.\n", "This is because in higher fidelity environments (and the real-world) a C2 server would not be accessible by a private network blue agent and the C2 Server would already be in place before the an adversary (Red Agent) starts.\n", "\n", - "The cells below install and runs the C2 Server on client_1 directly via the simulation API." + "The cells below install and run the C2 Server on client_1 directly via the simulation API." ] }, { @@ -1188,7 +1188,7 @@ "source": [ "#### Shutting down the node infected with a C2 Beacon.\n", "\n", - "Another way a blue agent can prevent the C2 suite is by shutting down the C2 beacon's host node. Whilst not as effective as the previous option, dependant on the situation (such as multiple malicious applications) or other scenarios it may be more timestep efficient for a blue agent to shut down a node directly." + "Another way a blue agent can prevent the C2 suite is by shutting down the C2 beacon's host node. Whilst not as effective as the previous option, depending on the situation (such as multiple malicious applications) or other scenarios it may be more timestep efficient for a blue agent to shut down a node directly." ] }, { @@ -1216,7 +1216,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The code cell below uses the custom blue agent defined at the start of this section perform a ``NODE_SHUT_DOWN`` action on the web server." + "The code cell below uses the custom blue agent defined at the start of this section to perform a ``NODE_SHUT_DOWN`` action on the web server." ] }, { @@ -1233,7 +1233,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Which we can see the effects of after another timestep and looking at the web_server's operating state & the OBS differences." + "Which we can see the effects of after another timestep and looking at the web server's operating state & the OBS differences." ] }, { @@ -1454,7 +1454,7 @@ "\n", "In the case of the C2 Beacon, the C2 Server's IP address must be supplied before the C2 beacon will be able to perform any other actions (including ``APPLICATION EXECUTE``).\n", "\n", - "If the network contains multiple C2 Servers then it's also possible to switch to different C2 servers mid-episode which is demonstrated in the below code cells." + "If the network contains multiple C2 Servers then it's also possible to switch to a different C2 servers mid-episode which is demonstrated in the below code cells." ] }, { @@ -1544,7 +1544,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "After six timesteps the client_1 server will recognise the C2 beacon's previous connection as dead and clear its connections. (This is dependant on the ``Keep Alive Frequency`` setting.)" + "After six timesteps the client_1 server will recognise the C2 beacon's previous connection as dead and clear its connections. (This is dependent on the ``Keep Alive Frequency`` setting.)" ] }, { diff --git a/src/primaite/simulator/system/applications/red_applications/c2/abstract_c2.py b/src/primaite/simulator/system/applications/red_applications/c2/abstract_c2.py index 0d7bbf1f..7e9de77e 100644 --- a/src/primaite/simulator/system/applications/red_applications/c2/abstract_c2.py +++ b/src/primaite/simulator/system/applications/red_applications/c2/abstract_c2.py @@ -75,7 +75,7 @@ class AbstractC2(Application, identifier="AbstractC2"): keep_alive_inactivity: int = 0 """Indicates how many timesteps since the last time the c2 application received a keep alive.""" - class _C2_Opts(BaseModel): + class C2_Opts(BaseModel): """A Pydantic Schema for the different C2 configuration options.""" keep_alive_frequency: int = Field(default=5, ge=1) @@ -87,7 +87,7 @@ class AbstractC2(Application, identifier="AbstractC2"): masquerade_port: Port = Field(default=Port.HTTP) """The currently chosen port that the C2 traffic is masquerading as. Defaults at HTTP.""" - c2_config: _C2_Opts = _C2_Opts() + c2_config: C2_Opts = C2_Opts() """ Holds the current configuration settings of the C2 Suite. diff --git a/src/primaite/simulator/system/applications/red_applications/c2/c2_beacon.py b/src/primaite/simulator/system/applications/red_applications/c2/c2_beacon.py index 393512db..fa0271e5 100644 --- a/src/primaite/simulator/system/applications/red_applications/c2/c2_beacon.py +++ b/src/primaite/simulator/system/applications/red_applications/c2/c2_beacon.py @@ -493,8 +493,9 @@ class C2Beacon(AbstractC2, identifier="C2Beacon"): ) # Converts a singular terminal command: [RequestFormat] into a list with one element [[RequestFormat]] + # Checks the first element - if this element is a str then there must be multiple commands. command_opts.commands = ( - [command_opts.commands] if not isinstance(command_opts.commands, list) else command_opts.commands + [command_opts.commands] if isinstance(command_opts.commands[0], str) else command_opts.commands ) for index, given_command in enumerate(command_opts.commands): diff --git a/tests/integration_tests/system/red_applications/test_c2_suite_integration.py b/tests/integration_tests/system/red_applications/test_c2_suite_integration.py index 910f4760..9d12f2cf 100644 --- a/tests/integration_tests/system/red_applications/test_c2_suite_integration.py +++ b/tests/integration_tests/system/red_applications/test_c2_suite_integration.py @@ -257,27 +257,37 @@ def test_c2_suite_terminal_command_file_creation(basic_network): # Testing that we can create the test file and folders via the terminal command (Local C2 Terminal). # Local file/folder creation commands. - file_create_command = { - "commands": [ - ["file_system", "create", "folder", "test_folder"], - ["file_system", "create", "file", "test_folder", "test_file", "True"], - ], + folder_create_command = { + "commands": ["file_system", "create", "folder", "test_folder"], "username": "admin", "password": "admin", "ip_address": None, } + c2_server.send_command(C2Command.TERMINAL, command_options=folder_create_command) + file_create_command = { + "commands": ["file_system", "create", "file", "test_folder", "test_file", "True"], + "username": "admin", + "password": "admin", + "ip_address": None, + } c2_server.send_command(C2Command.TERMINAL, command_options=file_create_command) assert computer_b.software_manager.file_system.access_file(folder_name="test_folder", file_name="test_file") == True assert c2_beacon.terminal_session is not None # Testing that we can create the same test file/folders via on node 3 via a remote terminal. + file_remote_create_command = { + "commands": [ + ["file_system", "create", "folder", "test_folder"], + ["file_system", "create", "file", "test_folder", "test_file", "True"], + ], + "username": "admin", + "password": "admin", + "ip_address": "192.168.255.3", + } - # node_c's IP is 192.168.255.3 - file_create_command.update({"ip_address": "192.168.255.3"}) - - c2_server.send_command(C2Command.TERMINAL, command_options=file_create_command) + c2_server.send_command(C2Command.TERMINAL, command_options=file_remote_create_command) assert computer_c.software_manager.file_system.access_file(folder_name="test_folder", file_name="test_file") == True assert c2_beacon.terminal_session is not None @@ -435,11 +445,15 @@ def test_c2_suite_acl_bypass(basic_network): # Confirming that we can send commands + http_folder_create_command = { + "commands": ["file_system", "create", "folder", "test_folder"], + "username": "admin", + "password": "admin", + "ip_address": None, + } + c2_server.send_command(C2Command.TERMINAL, command_options=http_folder_create_command) http_file_create_command = { - "commands": [ - ["file_system", "create", "folder", "test_folder"], - ["file_system", "create", "file", "test_folder", "http_test_file", "True"], - ], + "commands": ["file_system", "create", "file", "test_folder", "http_test_file", "true"], "username": "admin", "password": "admin", "ip_address": None,