From 592e1a3610c2849e8873a9e372a6774ef9b95df7 Mon Sep 17 00:00:00 2001 From: Czar Echavez Date: Wed, 13 Dec 2023 11:56:25 +0000 Subject: [PATCH] #2059: apply suggestions from PR + adding another test that checks for dos affecting green agent --- .../applications/red_applications/dos_bot.py | 4 +- .../services/database/database_service.py | 4 + .../simulator/system/services/service.py | 2 +- .../test_dos_bot_and_server.py | 75 ++++++++++++++++++- 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/primaite/simulator/system/applications/red_applications/dos_bot.py b/src/primaite/simulator/system/applications/red_applications/dos_bot.py index 84e0abb2..dfc48dd3 100644 --- a/src/primaite/simulator/system/applications/red_applications/dos_bot.py +++ b/src/primaite/simulator/system/applications/red_applications/dos_bot.py @@ -49,7 +49,7 @@ class DoSBot(DatabaseClient, Application): port_scan_p_of_success: float = 0.1 """Probability of port scanning being sucessful.""" - dos_intensity: float = 1 + dos_intensity: float = 1.0 """How much of the max sessions will be used by the DoS when attacking.""" def __init__(self, **kwargs): @@ -92,7 +92,7 @@ class DoSBot(DatabaseClient, Application): payload: Optional[str] = None, repeat: bool = False, port_scan_p_of_success: float = 0.1, - dos_intensity: float = 1, + dos_intensity: float = 1.0, max_sessions: int = 1000, ): """ diff --git a/src/primaite/simulator/system/services/database/database_service.py b/src/primaite/simulator/system/services/database/database_service.py index 7d313068..6f333091 100644 --- a/src/primaite/simulator/system/services/database/database_service.py +++ b/src/primaite/simulator/system/services/database/database_service.py @@ -143,6 +143,10 @@ class DatabaseService(Service): status_code = 500 # Default internal server error if self.operating_state == ServiceOperatingState.RUNNING: status_code = 503 # service unavailable + if self.health_state_actual == SoftwareHealthState.OVERWHELMED: + self.sys_log.error( + f"{self.name}: Connect request for {connection_id=} declined. Service is at capacity." + ) if self.health_state_actual == SoftwareHealthState.GOOD: if self.password == password: status_code = 200 # ok diff --git a/src/primaite/simulator/system/services/service.py b/src/primaite/simulator/system/services/service.py index 3155a4bd..d45ef3a6 100644 --- a/src/primaite/simulator/system/services/service.py +++ b/src/primaite/simulator/system/services/service.py @@ -58,7 +58,7 @@ class Service(IOSoftware): if not super()._can_perform_action(): return False - if self.operating_state is not self.operating_state.RUNNING: + if self.operating_state is not ServiceOperatingState.RUNNING: # service is not running _LOGGER.error(f"Cannot perform action: {self.name} is {self.operating_state.name}") return False diff --git a/tests/integration_tests/system/red_applications/test_dos_bot_and_server.py b/tests/integration_tests/system/red_applications/test_dos_bot_and_server.py index 2828cc25..85028d75 100644 --- a/tests/integration_tests/system/red_applications/test_dos_bot_and_server.py +++ b/tests/integration_tests/system/red_applications/test_dos_bot_and_server.py @@ -3,10 +3,13 @@ from typing import Tuple import pytest +from primaite.simulator.network.container import Network from primaite.simulator.network.hardware.nodes.computer import Computer +from primaite.simulator.network.hardware.nodes.router import ACLAction, Router from primaite.simulator.network.hardware.nodes.server import Server from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.system.applications.application import ApplicationOperatingState +from primaite.simulator.system.applications.database_client import DatabaseClient from primaite.simulator.system.applications.red_applications.dos_bot import DoSAttackStage, DoSBot from primaite.simulator.system.services.database.database_service import DatabaseService from primaite.simulator.system.software import SoftwareHealthState @@ -25,7 +28,7 @@ def dos_bot_and_db_server(client_server) -> Tuple[DoSBot, Computer, DatabaseServ target_port=Port.POSTGRES_SERVER, ) - # Install FTP Server service on server + # Install DB Server service on server server.software_manager.install(DatabaseService) db_server_service: DatabaseService = server.software_manager.software.get("DatabaseService") db_server_service.start() @@ -33,6 +36,43 @@ def dos_bot_and_db_server(client_server) -> Tuple[DoSBot, Computer, DatabaseServ return dos_bot, computer, db_server_service, server +@pytest.fixture(scope="function") +def dos_bot_db_server_green_client(example_network) -> Network: + network: Network = example_network + + router_1: Router = example_network.get_node_by_hostname("router_1") + router_1.acl.add_rule( + action=ACLAction.PERMIT, src_port=Port.POSTGRES_SERVER, dst_port=Port.POSTGRES_SERVER, position=0 + ) + + client_1: Computer = network.get_node_by_hostname("client_1") + client_2: Computer = network.get_node_by_hostname("client_2") + server: Server = network.get_node_by_hostname("server_1") + + # install DoS bot on client 1 + client_1.software_manager.install(DoSBot) + + dos_bot: DoSBot = client_1.software_manager.software.get("DoSBot") + dos_bot.configure( + target_ip_address=IPv4Address(server.nics.get(next(iter(server.nics))).ip_address), + target_port=Port.POSTGRES_SERVER, + ) + + # install db server service on server + server.software_manager.install(DatabaseService) + db_server_service: DatabaseService = server.software_manager.software.get("DatabaseService") + db_server_service.start() + + # Install DB client (green) on client 2 + client_2.software_manager.install(DatabaseClient) + + database_client: DatabaseClient = client_2.software_manager.software.get("DatabaseClient") + database_client.configure(server_ip_address=IPv4Address("192.168.0.1")) + database_client.run() + + return network + + def test_repeating_dos_attack(dos_bot_and_db_server): dos_bot, computer, db_server_service, server = dos_bot_and_db_server @@ -105,3 +145,36 @@ def test_dos_bot_database_service_connection(dos_bot_and_db_server): assert len(dos_bot.connections) == db_server_service.max_sessions assert len(db_server_service.connections) == db_server_service.max_sessions assert len(dos_bot.connections) == db_server_service.max_sessions + + +def test_dos_blocks_green_agent_connection(dos_bot_db_server_green_client): + network: Network = dos_bot_db_server_green_client + + client_1: Computer = network.get_node_by_hostname("client_1") + dos_bot: DoSBot = client_1.software_manager.software.get("DoSBot") + + client_2: Computer = network.get_node_by_hostname("client_2") + green_db_client: DatabaseClient = client_2.software_manager.software.get("DatabaseClient") + + server: Server = network.get_node_by_hostname("server_1") + db_server_service: DatabaseService = server.software_manager.software.get("DatabaseService") + + assert db_server_service.health_state_actual is SoftwareHealthState.GOOD + + dos_bot.port_scan_p_of_success = 1 + dos_bot.repeat = False + dos_bot.run() + + # DoS bot fills up connection of db server service + assert len(dos_bot.connections) == db_server_service.max_sessions + assert len(db_server_service.connections) == db_server_service.max_sessions + assert len(dos_bot.connections) == db_server_service.max_sessions + assert len(green_db_client.connections) == 0 + + assert dos_bot.attack_stage is DoSAttackStage.COMPLETED + # db server service is overwhelmed + assert db_server_service.health_state_actual is SoftwareHealthState.OVERWHELMED + + # green agent tries to connect but fails because service is overwhelmed + assert green_db_client.connect() is False + assert len(green_db_client.connections) == 0