#2689 Added C2 Sequence diagram to docs and added additional ftp_client request tests.

This commit is contained in:
Archer Bowen
2024-08-16 11:51:38 +01:00
parent 849cb20f35
commit 83b8206ce0
7 changed files with 78 additions and 6 deletions

BIN
docs/_static/c2_sequence.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -99,6 +99,12 @@ However, each host implements it's own receive methods.
- Sends C2 Commands to the C2 Beacon via ``C2Payload.INPUT``.
- Receives the RequestResponse of the C2 Commands executed by C2 Beacon via ``C2Payload.OUTPUT``.
The sequence diagram below clarifies the functionality of both applications:
.. image:: ../_static/c2_sequence.png
:width: 500
:align: center
For further details and more in-depth examples please refer to the ``Command-&-Control notebook``

View File

@@ -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 a 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 server mid-episode which is demonstrated in the below code cells."
]
},
{

View File

@@ -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 _C2Opts(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: _C2Opts = _C2Opts()
"""
Holds the current configuration settings of the C2 Suite.

View File

@@ -208,7 +208,7 @@ class C2Server(AbstractC2, identifier="C2Server"):
grants more opportunity to the blue agent to prevent attacks.
Additionally, future editions of primAITE may expand the C2 repertoire to allow for
more complex red agent behaviour such as file extraction, establishing further fall back channels
more complex red agent behaviour such as establishing further fall back channels
or introduce red applications that are only installable via C2 Servers. (T1105)
For more information on the impact of these commands please refer to the terminal

View File

@@ -52,7 +52,7 @@ class FTPClient(FTPServiceABC):
dest_ip = request[-1].get("dest_ip_address")
dest_ip = None if dest_ip is None else IPv4Address(dest_ip)
# TODO: Confirm that the default values lead to a safe failure.
# Missing FTP Options results is an automatic failure.
src_folder = request[-1].get("src_folder_name", None)
src_file_name = request[-1].get("src_file_name", None)
dest_folder = request[-1].get("dest_folder_name", None)
@@ -63,7 +63,10 @@ class FTPClient(FTPServiceABC):
f"{self.name}: Received a FTP Request to transfer file: {src_file_name} to Remote IP: {dest_ip}."
)
return RequestResponse(
status="failure", data={"reason": "Unable to locate requested file on local file system."}
status="failure",
data={
"reason": "Unable to locate given file on local file system. Perhaps given options are invalid?"
},
)
return RequestResponse.from_bool(

View File

@@ -3,6 +3,7 @@ from typing import Tuple
import pytest
from primaite.interface.request import RequestResponse
from primaite.simulator.network.hardware.nodes.host.computer import Computer
from primaite.simulator.network.hardware.nodes.host.server import Server
from primaite.simulator.system.services.ftp.ftp_client import FTPClient
@@ -105,3 +106,65 @@ def test_ftp_client_tries_to_connect_to_offline_server(ftp_client_and_ftp_server
# client should have retrieved the file
assert ftp_client.file_system.get_file(folder_name="downloads", file_name="test_file.txt") is None
def test_ftp_client_store_file_in_server_via_request_success(ftp_client_and_ftp_server):
"""
Test checks to see if the client can successfully store files in the backup server via the request manager.
"""
ftp_client, computer, ftp_server, server = ftp_client_and_ftp_server
assert ftp_client.operating_state == ServiceOperatingState.RUNNING
assert ftp_server.operating_state == ServiceOperatingState.RUNNING
# create file on ftp client
ftp_client.file_system.create_file(file_name="test_file.txt")
ftp_opts = {
"src_folder_name": "root",
"src_file_name": "test_file.txt",
"dest_folder_name": "client_1_backup",
"dest_file_name": "test_file.txt",
"dest_ip_address": server.network_interfaces.get(next(iter(server.network_interfaces))).ip_address,
}
ftp_client.apply_request(["send", ftp_opts])
assert ftp_server.file_system.get_file(folder_name="client_1_backup", file_name="test_file.txt")
def test_ftp_client_store_file_in_server_via_request_failure(ftp_client_and_ftp_server):
"""
Test checks to see if the client fails to store files in the backup server via the request manager.
"""
ftp_client, computer, ftp_server, server = ftp_client_and_ftp_server
assert ftp_client.operating_state == ServiceOperatingState.RUNNING
assert ftp_server.operating_state == ServiceOperatingState.RUNNING
# create file on ftp client
ftp_client.file_system.create_file(file_name="test_file.txt")
# Purposefully misconfigured FTP Options
ftp_opts = {
"src_folder_name": "root",
"dest_ip_address": server.network_interfaces.get(next(iter(server.network_interfaces))).ip_address,
}
request_response: RequestResponse = ftp_client.apply_request(["send", ftp_opts])
assert request_response.status == "failure"
# Purposefully misconfigured FTP Options
ftp_opts = {
"src_folder_name": "root",
"src_file_name": "not_a_real_file.txt",
"dest_folder_name": "client_1_backup",
"dest_file_name": "test_file.txt",
"dest_ip_address": server.network_interfaces.get(next(iter(server.network_interfaces))).ip_address,
}
request_response: RequestResponse = ftp_client.apply_request(["send", ftp_opts])
assert request_response.status == "failure"