#2689 Added C2 Sequence diagram to docs and added additional ftp_client request tests.
This commit is contained in:
BIN
docs/_static/c2_sequence.png
vendored
Normal file
BIN
docs/_static/c2_sequence.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
@@ -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``
|
||||
|
||||
|
||||
@@ -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."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user