diff --git a/agents/agent_utils.py b/agents/agent_utils.py index cde0afa..13c4e89 100644 --- a/agents/agent_utils.py +++ b/agents/agent_utils.py @@ -10,7 +10,7 @@ from os import path sys.path.append(path.dirname(path.dirname(path.dirname( path.abspath(__file__) )))) #with the path fixed, we can import now -from env.game_components import Action, ActionType, GameState, Observation, IP, Network +from AIDojoCoordinator.game_components import Action, ActionType, GameState, Observation, IP, Network import ipaddress def generate_valid_actions_concepts(state: GameState)->list: @@ -22,12 +22,12 @@ def generate_valid_actions_concepts(state: GameState)->list: # TODO ADD neighbouring networks # Only scan local networks from local hosts if network.is_private() and source_host.is_private(): - valid_actions.add(Action(ActionType.ScanNetwork, params={"target_network": network, "source_host": source_host,})) + valid_actions.add(Action(ActionType.ScanNetwork, parameters={"target_network": network, "source_host": source_host,})) # Service Scans for host in state.known_hosts: # Do not try to scan a service from hosts outside local networks towards local networks if host.is_private() and source_host.is_private(): - valid_actions.add(Action(ActionType.FindServices, params={"target_host": host, "source_host": source_host,})) + valid_actions.add(Action(ActionType.FindServices, parameters={"target_host": host, "source_host": source_host,})) # Service Exploits for host, service_list in state.known_services.items(): # Only exploit local services from local hosts @@ -37,52 +37,65 @@ def generate_valid_actions_concepts(state: GameState)->list: for service in service_list: # Do not consider local services, which are internal to the host if not service.is_local: - valid_actions.add(Action(ActionType.ExploitService, params={"target_host": host,"target_service": service,"source_host": source_host,})) + valid_actions.add(Action(ActionType.ExploitService, parameters={"target_host": host,"target_service": service,"source_host": source_host,})) # Find Data Scans for host in state.controlled_hosts: - valid_actions.add(Action(ActionType.FindData, params={"target_host": host, "source_host": host})) + valid_actions.add(Action(ActionType.FindData, parameters={"target_host": host, "source_host": host})) # Data Exfiltration for source_host, data_list in state.known_data.items(): for data in data_list: for target_host in state.controlled_hosts: if target_host != source_host: - valid_actions.add(Action(ActionType.ExfiltrateData, params={"target_host": target_host, "source_host": source_host, "data": data})) + valid_actions.add(Action(ActionType.ExfiltrateData, parameters={"target_host": target_host, "source_host": source_host, "data": data})) return list(valid_actions) -def generate_valid_actions(state: GameState)->list: +def generate_valid_actions(state: GameState, include_blocks=False)->list: """Function that generates a list of all valid actions in a given state""" valid_actions = set() + def is_fw_blocked(state, src_ip, dst_ip)->bool: + blocked = False + try: + blocked = dst_ip in state.known_blocks[src_ip] + except KeyError: + pass #this src ip has no known blocks + return blocked + for src_host in state.controlled_hosts: #Network Scans for network in state.known_networks: # TODO ADD neighbouring networks - valid_actions.add(Action(ActionType.ScanNetwork, params={"target_network": network, "source_host": src_host,})) + valid_actions.add(Action(ActionType.ScanNetwork, parameters={"target_network": network, "source_host": src_host,})) # Service Scans for host in state.known_hosts: - valid_actions.add(Action(ActionType.FindServices, params={"target_host": host, "source_host": src_host,})) + if not is_fw_blocked(state, src_host,host): + valid_actions.add(Action(ActionType.FindServices, parameters={"target_host": host, "source_host": src_host,})) # Service Exploits for host, service_list in state.known_services.items(): - for service in service_list: - valid_actions.add(Action(ActionType.ExploitService, params={"target_host": host,"target_service": service,"source_host": src_host,})) + if not is_fw_blocked(state, src_host,host): + for service in service_list: + valid_actions.add(Action(ActionType.ExploitService, parameters={"target_host": host,"target_service": service,"source_host": src_host,})) # Data Scans for host in state.controlled_hosts: - valid_actions.add(Action(ActionType.FindData, params={"target_host": host, "source_host": host})) + if not is_fw_blocked(state, src_host,host): + valid_actions.add(Action(ActionType.FindData, parameters={"target_host": host, "source_host": host})) # Data Exfiltration for src_host, data_list in state.known_data.items(): for data in data_list: for trg_host in state.controlled_hosts: if trg_host != src_host: - valid_actions.add(Action(ActionType.ExfiltrateData, params={"target_host": trg_host, "source_host": src_host, "data": data})) + if not is_fw_blocked(state, src_host,trg_host): + valid_actions.add(Action(ActionType.ExfiltrateData, parameters={"target_host": trg_host, "source_host": src_host, "data": data})) - # BlockIP - for src_host in state.controlled_hosts: - for target_host in state.controlled_hosts: - for blocked_ip in state.known_hosts: - valid_actions.add(Action(ActionType.BlockIP, {"target_host":target_host, "source_host":src_host, "blocked_host":blocked_ip})) - - + if include_blocks: + # BlockIP + if include_blocks: + for src_host in state.controlled_hosts: + for target_host in state.controlled_hosts: + if not is_fw_blocked(state, src_host,target_host): + for blocked_ip in state.known_hosts: + valid_actions.add(Action(ActionType.BlockIP, {"target_host":target_host, "source_host":src_host, "blocked_host":blocked_ip})) return list(valid_actions) def state_as_ordered_string(state:GameState)->str: @@ -97,6 +110,9 @@ def state_as_ordered_string(state:GameState)->str: ret += "},data:{" for host in sorted(state.known_data.keys()): ret += f"{host}:[{','.join([str(x) for x in sorted(state.known_data[host])])}]" + ret += "},blocks:{" + for host in sorted(state.known_blocks.keys()): + ret += f"{host}:[{','.join([str(x) for x in sorted(state.known_blocks[host])])}]" ret += "}" return ret @@ -480,7 +496,7 @@ def convert_concepts_to_actions(action, concept_mapping, logger): if src_host_concept == host_concept: new_src_host = concept_mapping['controlled_hosts'][host_concept] - action = Action(ActionType.ExploitService, params={"target_host": new_target_host, "target_service": new_target_service, "source_host": new_src_host}) + action = Action(ActionType.ExploitService, parameters={"target_host": new_target_host, "target_service": new_target_service, "source_host": new_src_host}) elif action._type == ActionType.ExfiltrateData: # parameters = { @@ -508,7 +524,7 @@ def convert_concepts_to_actions(action, concept_mapping, logger): data_concept = action.parameters['data'] new_data = data_concept - action = Action(ActionType.ExfiltrateData, params={"target_host": new_target_host, "source_host": new_src_host, "data": new_data}) + action = Action(ActionType.ExfiltrateData, parameters={"target_host": new_target_host, "source_host": new_src_host, "data": new_data}) elif action._type == ActionType.FindData: # parameters = { @@ -529,7 +545,7 @@ def convert_concepts_to_actions(action, concept_mapping, logger): if src_host_concept == host_concept: new_src_host = concept_mapping['controlled_hosts'][host_concept] - action = Action(ActionType.FindData, params={"target_host": new_target_host, "source_host": new_src_host}) + action = Action(ActionType.FindData, parameters={"target_host": new_target_host, "source_host": new_src_host}) elif action._type == ActionType.ScanNetwork: target_net_concept = action.parameters['target_network'] @@ -545,7 +561,7 @@ def convert_concepts_to_actions(action, concept_mapping, logger): for host_concept in concept_mapping['controlled_hosts']: if src_host_concept == host_concept: new_src_host = concept_mapping['controlled_hosts'][host_concept] - action = Action(ActionType.ScanNetwork, params={"source_host": new_src_host, "target_network": new_target_network} ) + action = Action(ActionType.ScanNetwork, parameters={"source_host": new_src_host, "target_network": new_target_network} ) elif action._type == ActionType.FindServices: # parameters = { @@ -565,6 +581,6 @@ def convert_concepts_to_actions(action, concept_mapping, logger): for host_concept in concept_mapping['controlled_hosts']: if src_host_concept == host_concept: new_src_host = concept_mapping['controlled_hosts'][host_concept] - action = Action(ActionType.FindServices, params={"source_host": new_src_host, "target_host": new_target_host} ) + action = Action(ActionType.FindServices, parameters={"source_host": new_src_host, "target_host": new_target_host} ) return action \ No newline at end of file diff --git a/agents/attackers/concepts_q_learning/conceptual_q_agent.py b/agents/attackers/concepts_q_learning/conceptual_q_agent.py index 2980b24..1877445 100644 --- a/agents/attackers/concepts_q_learning/conceptual_q_agent.py +++ b/agents/attackers/concepts_q_learning/conceptual_q_agent.py @@ -2,24 +2,21 @@ # Arti # Sebastian Garcia. sebastian.garcia@agents.fel.cvut.cz import sys -from os import path, makedirs import numpy as np import random import pickle import argparse import logging -# This is used so the agent can see the environment and game component -sys.path.append(path.dirname(path.dirname(path.dirname(path.dirname(path.dirname(path.abspath(__file__) ) ) )))) -sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__) )))) +import mlflow +import subprocess +from os import path, makedirs +from AIDojoCoordinator.game_components import Action, Observation, GameState, AgentStatus # This is used so the agent can see the environment and game component # with the path fixed, we can import now -from env.game_components import Action, Observation, GameState +sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__) )))) from base_agent import BaseAgent from agent_utils import generate_valid_actions, state_as_ordered_string, convert_concepts_to_actions, convert_ips_to_concepts -import mlflow -import subprocess - class QAgent(BaseAgent): @@ -383,15 +380,15 @@ def play_game(self, observation_ip, episode_num, testing=False): test_end = test_observation.end test_info = test_observation.info - if test_info and test_info['end_reason'] == 'blocked': + if test_info and test_info['end_reason'] == AgentStatus.Fail: test_detected +=1 test_num_detected_steps += [num_steps] test_num_detected_returns += [reward] - elif test_info and test_info['end_reason'] == 'goal_reached': + elif test_info and test_info['end_reason'] == AgentStatus.Success: test_wins += 1 test_num_win_steps += [num_steps] test_num_win_returns += [reward] - elif test_info and test_info['end_reason'] == 'max_steps': + elif test_info and test_info['end_reason'] == AgentStatus.TimeoutReached: test_max_steps += 1 test_num_max_steps_steps += [num_steps] test_num_max_steps_returns += [reward] diff --git a/agents/attackers/double_q_learning/double_q_agent.py b/agents/attackers/double_q_learning/double_q_agent.py index 287d7c2..c8f4b17 100644 --- a/agents/attackers/double_q_learning/double_q_agent.py +++ b/agents/attackers/double_q_learning/double_q_agent.py @@ -14,8 +14,8 @@ import logging from torch.utils.tensorboard import SummaryWriter import time -from env.worlds.network_security_game import NetworkSecurityEnvironment -from env.game_components import Action, Observation, GameState +from AIDojoCoordinator.worlds.network_security_game import NetworkSecurityEnvironment +from AIDojoCoordinator.game_components import Action, Observation, GameState class DoubleQAgent: diff --git a/agents/attackers/gnn_reinforce/gnn_REINFORCE_agent.py b/agents/attackers/gnn_reinforce/gnn_REINFORCE_agent.py index c7e9abb..3140530 100644 --- a/agents/attackers/gnn_reinforce/gnn_REINFORCE_agent.py +++ b/agents/attackers/gnn_reinforce/gnn_REINFORCE_agent.py @@ -18,7 +18,7 @@ sys.path.append(path.dirname(path.dirname(path.dirname(path.dirname( path.dirname( path.abspath(__file__) ) ) )))) #with the path fixed, we can import now -from env.worlds.network_security_game import NetworkSecurityEnvironment +from AIDojoCoordinator.worlds.network_security_game import NetworkSecurityEnvironment os.environ['CUDA_VISIBLE_DEVICES'] = '-1' os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' diff --git a/agents/attackers/interactive_tui/assistant.py b/agents/attackers/interactive_tui/assistant.py index 0ce98dd..fe11f32 100644 --- a/agents/attackers/interactive_tui/assistant.py +++ b/agents/attackers/interactive_tui/assistant.py @@ -10,13 +10,8 @@ import jinja2 from tenacity import retry, stop_after_attempt -sys.path.append( - path.dirname(path.dirname(path.dirname(path.dirname(path.dirname(path.abspath(__file__)))))) -) -from env.game_components import ( - ActionType, - Observation, -) + +from AIDojoCoordinator.game_components import ActionType, Observation sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__))))) diff --git a/agents/attackers/interactive_tui/interactive_tui.py b/agents/attackers/interactive_tui/interactive_tui.py index 94ee988..fead246 100644 --- a/agents/attackers/interactive_tui/interactive_tui.py +++ b/agents/attackers/interactive_tui/interactive_tui.py @@ -1,29 +1,20 @@ # # Author: Maria Rigaki - maria.rigaki@aic.fel.cvut.cz -# -from textual.app import App, ComposeResult, Widget -from textual.widgets import Tree, Button, RichLog, Select, Input -from textual.containers import Vertical, VerticalScroll, Horizontal -from textual.validation import Function -from textual import on -from textual.reactive import reactive import sys -from os import path import os import logging import ipaddress import argparse import asyncio - +from textual.app import App, ComposeResult, Widget +from textual.widgets import Tree, Button, RichLog, Select, Input +from textual.containers import Vertical, VerticalScroll, Horizontal +from textual.validation import Function +from textual import on +from textual.reactive import reactive from assistant import LLMAssistant - -# This is used so the agent can see the environment and game components -sys.path.append( - path.dirname(path.dirname(path.dirname(path.dirname(path.dirname(path.abspath(__file__)))))) -) -from env.game_components import Network, IP -from env.game_components import ActionType, Action, GameState, Observation +from AIDojoCoordinator.game_components import Network, IP, ActionType, Action, GameState, Observation # This is used so the agent can see the BaseAgent sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -558,7 +549,7 @@ def generate_action(self, state: GameState) -> Action: self.network_input[:-3], mask=int(self.network_input[-2:]) ), } - action = Action(action_type=self.next_action, params=parameters) + action = Action(action_type=self.next_action, parameters=parameters) else: self.notify("Please provide valid inputs", severity="error") elif self.next_action in [ActionType.FindServices, ActionType.FindData]: @@ -567,7 +558,7 @@ def generate_action(self, state: GameState) -> Action: "source_host": IP(self.src_host_input), "target_host": IP(self.target_host_input), } - action = Action(action_type=self.next_action, params=parameters) + action = Action(action_type=self.next_action, parameters=parameters) else: self.notify("Please provide valid inputs", severity="error") elif self.next_action == ActionType.ExploitService: @@ -582,7 +573,7 @@ def generate_action(self, state: GameState) -> Action: "target_service": service, } action = Action( - action_type=self.next_action, params=parameters + action_type=self.next_action, parameters=parameters ) break else: @@ -600,7 +591,7 @@ def generate_action(self, state: GameState) -> Action: "data": datum, } action = Action( - action_type=self.next_action, params=parameters + action_type=self.next_action, parameters=parameters ) else: parameters = self.data_input diff --git a/agents/attackers/llm/llm_agent-2.py b/agents/attackers/llm/llm_agent-2.py index 64bf6ff..a9def62 100644 --- a/agents/attackers/llm/llm_agent-2.py +++ b/agents/attackers/llm/llm_agent-2.py @@ -8,8 +8,8 @@ sys.path.append(path.dirname(path.dirname(path.dirname(path.dirname( path.dirname( path.abspath(__file__) ) ) )))) -from env.worlds.network_security_game import NetworkSecurityEnvironment -from env.game_components import ActionType, Action, IP, Data, Network, Service +from AIDojoCoordinator.worlds.network_security_game import NetworkSecurityEnvironment +from AIDojoCoordinator.game_components import ActionType, Action, IP, Data, Network, Service import openai from tenacity import retry, stop_after_attempt diff --git a/agents/attackers/llm/llm_agent-3.py b/agents/attackers/llm/llm_agent-3.py index 09c8e0b..c1f997c 100644 --- a/agents/attackers/llm/llm_agent-3.py +++ b/agents/attackers/llm/llm_agent-3.py @@ -7,8 +7,8 @@ sys.path.append(path.dirname(path.dirname(path.dirname(path.dirname( path.dirname( path.abspath(__file__) ) ) )))) -from env.worlds.network_security_game import NetworkSecurityEnvironment -from env.game_components import ActionType, Action, IP, Data, Network, Service +from AIDojoCoordinator.worlds.network_security_game import NetworkSecurityEnvironment +from AIDojoCoordinator.game_components import ActionType, Action, IP, Data, Network, Service import openai from tenacity import retry, stop_after_attempt diff --git a/agents/attackers/llm/llm_agent.py b/agents/attackers/llm/llm_agent.py index 36a366e..f98be5f 100644 --- a/agents/attackers/llm/llm_agent.py +++ b/agents/attackers/llm/llm_agent.py @@ -7,8 +7,8 @@ sys.path.append(path.dirname(path.dirname(path.dirname(path.dirname( path.dirname( path.abspath(__file__) ) ) )))) -from env.worlds.network_security_game import NetworkSecurityEnvironment -from env.game_components import ActionType, Action, IP, Data, Network, Service +from AIDojoCoordinator.worlds.network_security_game import NetworkSecurityEnvironment +from AIDojoCoordinator.game_components import ActionType, Action, IP, Data, Network, Service import openai from tenacity import retry, stop_after_attempt diff --git a/agents/attackers/llm_embed/llm_embed.py b/agents/attackers/llm_embed/llm_embed.py index be57cc2..694819c 100644 --- a/agents/attackers/llm_embed/llm_embed.py +++ b/agents/attackers/llm_embed/llm_embed.py @@ -11,8 +11,8 @@ from os import path sys.path.append(path.dirname(path.dirname(path.dirname(path.dirname( path.dirname( path.abspath(__file__) ) ) )))) -from env.worlds.network_security_game import NetworkSecurityEnvironment -from env.game_components import Action, ActionType +from AIDojoCoordinator.worlds.network_security_game import NetworkSecurityEnvironment +from AIDojoCoordinator.game_components import Action, ActionType import numpy as np import torch diff --git a/agents/attackers/llm_embed_dqn/llm_embed_dqn.py b/agents/attackers/llm_embed_dqn/llm_embed_dqn.py index 12b8184..b795439 100644 --- a/agents/attackers/llm_embed_dqn/llm_embed_dqn.py +++ b/agents/attackers/llm_embed_dqn/llm_embed_dqn.py @@ -13,8 +13,8 @@ from os import path sys.path.append(path.dirname(path.dirname(path.dirname(path.dirname( path.dirname( path.abspath(__file__) ) ) )))) -from env.worlds.network_security_game import NetworkSecurityEnvironment -from env.game_components import Action, ActionType +from AIDojoCoordinator.worlds.network_security_game import NetworkSecurityEnvironment +from AIDojoCoordinator.game_components import Action, ActionType from sentence_transformers import SentenceTransformer import numpy as np diff --git a/agents/attackers/llm_qa/llm_agent_qa.py b/agents/attackers/llm_qa/llm_agent_qa.py index 862bcbf..015c6ba 100644 --- a/agents/attackers/llm_qa/llm_agent_qa.py +++ b/agents/attackers/llm_qa/llm_agent_qa.py @@ -5,13 +5,27 @@ """ import logging import argparse -from llm_action_planner import LLMActionPlanner import logging import numpy as np import pandas as pd import mlflow +import sys +from llm_action_planner import LLMActionPlanner +from os import path + +mlflow.set_tracking_uri("http://147.32.83.60") +mlflow.set_experiment("LLM_QA") + +from transformers import AutoModelForCausalLM, AutoTokenizer, GenerationConfig + +sys.path.append( + path.dirname(path.dirname(path.dirname(path.dirname(path.abspath(__file__))))) +) + +from AIDojoCoordinator.game_components import ActionType, Action, IP, Data, Network, Service -from env.game_components import ActionType +# This is used so the agent can see the BaseAgent +sys.path.append(path.dirname(path.dirname(path.abspath(__file__)))) from base_agent import BaseAgent mlflow.set_tracking_uri("http://147.32.83.60") diff --git a/agents/attackers/q_learning/q_agent.py b/agents/attackers/q_learning/q_agent.py index ae74cc1..3a18660 100644 --- a/agents/attackers/q_learning/q_agent.py +++ b/agents/attackers/q_learning/q_agent.py @@ -7,19 +7,15 @@ import pickle import argparse import logging +import mlflow +import subprocess from os import path, makedirs -# This is used so the agent can see the environment and game component -sys.path.append(path.dirname(path.dirname(path.dirname(path.dirname(path.dirname(path.abspath(__file__) ) ) )))) -sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__) )))) - -# This is used so the agent can see the environment and game component # with the path fixed, we can import now -from env.game_components import Action, Observation, GameState +from AIDojoCoordinator.game_components import Action, Observation, GameState, AgentStatus +sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__) )))) from base_agent import BaseAgent from agent_utils import generate_valid_actions, state_as_ordered_string -import mlflow -import subprocess class QAgent(BaseAgent): @@ -288,15 +284,15 @@ def play_game(self, observation, episode_num, testing=False): end = observation.end info = observation.info - if observation.info and observation.info['end_reason'] == 'blocked': + if observation.info and observation.info['end_reason'] == AgentStatus.Fail: detected +=1 num_detected_steps += [num_steps] num_detected_returns += [reward] - elif observation.info and observation.info['end_reason'] == 'goal_reached': + elif observation.info and observation.info['end_reason'] == AgentStatus.Success: wins += 1 num_win_steps += [num_steps] num_win_returns += [reward] - elif observation.info and observation.info['end_reason'] == 'max_steps': + elif observation.info and observation.info['end_reason'] == AgentStatus.TimeoutReached: max_steps += 1 num_max_steps_steps += [num_steps] num_max_steps_returns += [reward] diff --git a/agents/attackers/random/random_agent.py b/agents/attackers/random/random_agent.py index f307852..0e22159 100644 --- a/agents/attackers/random/random_agent.py +++ b/agents/attackers/random/random_agent.py @@ -2,18 +2,16 @@ # This agents just randomnly picks actions. No learning import sys import logging -import os -from random import choice import argparse import numpy as np import mlflow +from os import path, makedirs +from random import choice +from AIDojoCoordinator.game_components import Action, Observation, AgentStatus -# This is used so the agent can see the environment and game components -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__) ) ) )))) -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__) )))) +sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__) )))) # with the path fixed, we can import now -from env.game_components import Action, Observation from base_agent import BaseAgent from agent_utils import generate_valid_actions from datetime import datetime @@ -131,15 +129,15 @@ def select_action(self, observation:Observation)->Action: end = observation.end info = observation.info - if observation.info and observation.info['end_reason'] == 'blocked': + if observation.info and observation.info['end_reason'] == AgentStatus.Fail: detected +=1 num_detected_steps += [num_steps] num_detected_returns += [reward] - elif observation.info and observation.info['end_reason'] == 'goal_reached': + elif observation.info and observation.info['end_reason'] == AgentStatus.Success: wins += 1 num_win_steps += [num_steps] num_win_returns += [reward] - elif observation.info and observation.info['end_reason'] == 'max_steps': + elif observation.info and observation.info['end_reason'] == AgentStatus.TimeoutReached: max_steps += 1 num_max_steps_steps += [num_steps] num_max_steps_returns += [reward] diff --git a/agents/attackers/sarsa/sarsa_agent.py b/agents/attackers/sarsa/sarsa_agent.py index 8246408..382f378 100644 --- a/agents/attackers/sarsa/sarsa_agent.py +++ b/agents/attackers/sarsa/sarsa_agent.py @@ -8,16 +8,10 @@ import argparse import logging -# This is used so the agent can see the environment and game components -sys.path.append( - path.dirname(path.dirname(path.dirname(path.dirname(path.dirname(path.abspath(__file__)))))) -) +from AIDojoCoordinator.game_components import Action, GameState # importing agent utils and base agent sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__) )))) - -# This is used so the agent can see the environment and game component # with the path fixed, we can import now -from env.game_components import Action, GameState from base_agent import BaseAgent from agent_utils import generate_valid_actions, state_as_ordered_string @@ -104,7 +98,9 @@ def play_game(self, num_episodes=1, testing=False): testing_returns = [] for _ in range(args.eval_for): testing_returns.append(np.sum(self.play_episode(testing=True))) - self._logger.info(f"Eval after {episode} episodes: ={np.mean(testing_returns)}±{np.std(testing_returns)}") + self._logger.info(f"Eval after {episode} episodes: ={np.mean(testing_returns)}±{np.std(testing_returns)}") + if episode % args.store_models_every == 0 and episode != 0: + self.store_q_table(f'sarsa_agent_marl.experiment{args.experiment_id}-episodes-{episode}.pickle') returns = [] for _ in range(args.eval_for): returns.append(np.sum(self.play_episode(testing=True))) @@ -116,14 +112,17 @@ def play_game(self, num_episodes=1, testing=False): parser = argparse.ArgumentParser() parser.add_argument("--host", help="Host where the game server is", default="127.0.0.1", action='store', required=False) parser.add_argument("--port", help="Port where the game server is", default=9000, type=int, action='store', required=False) - parser.add_argument("--episodes", help="Sets number of testing episodes", default=5, type=int) - parser.add_argument("--eval_each", help="Sets periodic evaluation during training", default=250, type=int) - parser.add_argument("--eval_for", help="Sets length of periodic evaluation", default=200, type=int) + parser.add_argument("--episodes", help="Sets number of testing episodes", default=25000, type=int) + parser.add_argument("--eval_each", help="Sets periodic evaluation during training", default=5000, type=int) + parser.add_argument("--eval_for", help="Sets length of periodic evaluation", default=5000, type=int) parser.add_argument("--epsilon", help="Sets epsilon for exploration", default=0.2, type=float) parser.add_argument("--gamma", help="Sets gamma for Q learing", default=0.9, type=float) - parser.add_argument("--alpha", help="Sets alpha for learning rate", default=0.8, type=float) + parser.add_argument("--alpha", help="Sets alpha for learning rate", default=0.1, type=float) parser.add_argument("--logdir", help="Folder to store logs", default=path.join(path.dirname(path.abspath(__file__)), "logs")) parser.add_argument("--test_only", help="Only run testing", default=False, action='store_true') + parser.add_argument("--experiment_id", help="Id of the experiment to record into Mlflow.", default='sarsa_006_coordinatorV3', type=str) + parser.add_argument("--store_models_every", help="Store a model to disk every these number of episodes.", default=2000, type=int) + parser.add_argument("--previous_model", help="Store a model to disk every these number of episodes.", type=str) args = parser.parse_args() if not path.exists(args.logdir): @@ -134,8 +133,9 @@ def play_game(self, num_episodes=1, testing=False): agent = SARSAAgent(args.host, args.port, alpha=args.alpha, gamma=args.gamma, epsilon=args.epsilon) if args.test_only: - agent.load_q_table("./sarsa_agent_marl.pickle") + agent.load_q_table(args.previous_model) agent.play_game(args.episodes, testing=True) else: agent.play_game(args.episodes, testing=False) - agent.store_q_table("./sarsa_agent_marl.pickle") \ No newline at end of file + agent.store_q_table("./sarsa_agent_marl.pickle") + diff --git a/agents/attackers/scripted_attacker/scripted_attacker.py b/agents/attackers/scripted_attacker/scripted_attacker.py new file mode 100644 index 0000000..f1811a1 --- /dev/null +++ b/agents/attackers/scripted_attacker/scripted_attacker.py @@ -0,0 +1,85 @@ +import sys +import argparse +import logging +from os import path +import time + +from AIDojoCoordinator.game_components import Action, ActionType, IP, Network +sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__) )))) +#with the path fixed, we can import now +from base_agent import BaseAgent + +def winning_strat(host, port, delay=10): + agent1 = BaseAgent(host, port, role="Attacker") + obs1 = agent1.register() + time.sleep(delay) + print(obs1) + print("----------------------------") + # # network scan + obs1 = agent1.make_step(Action( + ActionType.ScanNetwork, + parameters={ + "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], + "target_network":Network("192.168.1.0", 24) + } + ) + ) + print(obs1) + time.sleep(delay) + print("----------------------------") + obs1 = agent1.make_step(Action( + ActionType.FindServices, + parameters={ + "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], + "target_host": IP("192.168.1.2") + } + ) + ) + print(obs1) + print("----------------------------") + time.sleep(delay) + obs1 = agent1.make_step(Action( + ActionType.ExploitService, + parameters={ + "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], + "target_host": IP("192.168.1.2"), + "target_service": list(obs1.state.known_services[IP("192.168.1.2")])[0] + } + ) + ) + print(obs1) + print("----------------------------") + time.sleep(delay) + obs1 = agent1.make_step(Action( + ActionType.FindData, + parameters={ + "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], + "target_host": IP("192.168.1.2"), + } + ) + ) + print(obs1) + print("----------------------------") + time.sleep(delay) + obs1 = agent1.make_step(Action( + ActionType.ExfiltrateData, + parameters={ + "source_host": IP("192.168.1.2"), + "target_host": IP("213.47.23.195"), + "data":list(obs1.state.known_data[IP("192.168.1.2")])[0] + } + ) + ) + print(obs1) + print("----------------------------") + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--host", help="Host where the game server is", default="127.0.0.1", action='store', required=False) + parser.add_argument("--port", help="Port where the game server is", default=9000, type=int, action='store', required=False) + parser.add_argument("--delay", help="Delay between actions (in seconds)", default=10, type=int, action='store', required=False) + + args = parser.parse_args() + log_filename = path.dirname(path.abspath(__file__)) + '/base_agent.log' + logging.basicConfig(filename="scripted_attacker.log", filemode='w', format='%(asctime)s %(name)s %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.DEBUG) + winning_strat(args.host, args.port, args.delay) \ No newline at end of file diff --git a/agents/base_agent.py b/agents/base_agent.py index cdbdb4e..64668cc 100644 --- a/agents/base_agent.py +++ b/agents/base_agent.py @@ -1,19 +1,16 @@ # Author: Ondrej Lukas, ondrej.lukas@aic.cvut.cz # Basic agent class that is to be extended in each agent classes import sys -import argparse import logging from os import path import socket import json -from abc import ABC - +from abc import ABC # This is used so the agent can see the environment and game components sys.path.append(path.dirname(path.dirname( path.dirname( path.abspath(__file__) ) ) )) -from env.game_components import Action, GameState, Observation, ActionType, GameStatus,AgentInfo, IP, Network, Data - +from AIDojoCoordinator.game_components import Action, GameState, Observation, ActionType, GameStatus,AgentInfo class BaseAgent(ABC): """ @@ -101,7 +98,7 @@ def _receive_data(socket)->tuple: return GameStatus.from_string(status), observation, message if isinstance(data, Action): - data = data.as_json() + data = data.to_json() else: raise ValueError("Incorrect data type! Data should be ONLY of type Action") @@ -116,7 +113,7 @@ def register(self)->Observation: try: self._logger.info(f'Registering agent as {self.role}') status, observation_dict, message = self.communicate(Action(ActionType.JoinGame, - params={"agent_info":AgentInfo(self.__class__.__name__,self.role)})) + parameters={"agent_info":AgentInfo(self.__class__.__name__,self.role)})) self._logger.info(f'\tRegistering agent as {status, observation_dict, message}') if status is GameStatus.CREATED: self._logger.info(f"\tRegistration successful! {message}") @@ -132,294 +129,10 @@ def request_game_reset(self)->Observation: Method for requesting restart of the game. """ self._logger.debug("Requesting game reset") - status, observation_dict, message = self.communicate(Action(ActionType.ResetGame)) + status, observation_dict, message = self.communicate(Action(ActionType.ResetGame, {})) if status: self._logger.debug('\tReset successful') return Observation(GameState.from_dict(observation_dict["state"]), observation_dict["reward"], observation_dict["end"], message) else: self._logger.error(f'\rReset failed! (status: {status}, msg:{message}') return None - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument("--host", help="Host where the game server is", default="127.0.0.1", action='store', required=False) - parser.add_argument("--port", help="Port where the game server is", default=9000, type=int, action='store', required=False) - - args = parser.parse_args() - log_filename = path.dirname(path.abspath(__file__)) + '/base_agent.log' - logging.basicConfig(filename=log_filename, filemode='w', format='%(asctime)s %(name)s %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.DEBUG) - - # # ####################################### - # # ATTACKER X ATTACKER - # # ####################################### - - # #register both agents - # agent1 = BaseAgent(args.host, args.port, role="Attacker") - # obs1 = agent1.register() - # agent2 = BaseAgent(args.host, args.port, role="Attacker") - # obs2 = agent2.register() - - # # network scans - both agents - # obs1 = agent1.make_step(Action( - # ActionType.ScanNetwork, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - # "target_network":Network("192.168.1.0", 24) - # } - # ) - # ) - # obs2 = agent2.make_step(Action( - # ActionType.ScanNetwork, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs2.state.controlled_hosts))[0], - # "target_network":Network("192.168.1.0", 24) - # } - # ) - # ) - # # service scans - both agents - # obs1 = agent1.make_step(Action( - # ActionType.FindServices, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - # "target_host":IP("192.168.1.2") - # } - # ) - # ) - # obs2 = agent2.make_step(Action( - # ActionType.FindServices, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs2.state.controlled_hosts))[0], - # "target_host":IP("192.168.1.2") - # } - # ) - # ) - # # agent 1 exploit - # host, services = [(k,v) for k,v in obs1.state.known_services.items()][0] - # obs1 = agent1.make_step(Action( - # ActionType.ExploitService, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - # "target_host":host, - # "target_service":list(services)[0] - # } - # ) - # ) - # # agent 1 find data - # obs1 = agent1.make_step(Action( - # ActionType.FindData, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - # "target_host":host, - # } - # ) - # ) - - # # agent 1 exfiltrate data to C&C - # obs1 = agent1.make_step(Action( - # ActionType.ExfiltrateData, - # params={ - # "source_host": host, - # "target_host": IP("213.47.23.195"), - # "data": Data("User1","DataFromServer1") - # } - # ) - # ) - - # # agent 2 find data - # obs2 = agent2.make_step(Action( - # ActionType.FindData, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs2.state.controlled_hosts))[0], - # "target_host":host, - # } - # ) - # ) - # # agent 2 find data in C&C - # obs2 = agent2.make_step(Action( - # ActionType.FindData, - # params={ - # "source_host": IP("213.47.23.195"), - # "target_host": IP("213.47.23.195"), - # } - # ) - # ) - # print(obs1) - # print(obs2) - - - ######################################## - # ATTACKER X DEFENDER - ######################################## - - # # register both agents - # agent1 = BaseAgent(args.host, args.port, role="Attacker") - # obs1 = agent1.register() - # agent2 = BaseAgent(args.host, args.port, role="Defender") - # obs2 = agent2.register() - - # # attacker - scan net - # obs1 = agent1.make_step(Action( - # ActionType.ScanNetwork, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - # "target_network":Network("192.168.1.0", 24) - # } - # ) - # ) - - # # attacker - scan services - # obs1 = agent1.make_step(Action( - # ActionType.FindServices, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - # "target_host":IP("192.168.1.2") - # } - # ) - # ) - - # # agent 1 exploit - # host, services = [(k,v) for k,v in obs1.state.known_services.items()][0] - # obs1 = agent1.make_step(Action( - # ActionType.ExploitService, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - # "target_host":host, - # "target_service":list(services)[0] - # } - # ) - # ) - # # agent 1 find data - # obs1 = agent1.make_step(Action( - # ActionType.FindData, - # params={ - # "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - # "target_host":host, - # } - # ) - # ) - # # agent 2 block connection to C&C - # obs2 = agent2.make_step(Action( - # ActionType.BlockIP, - # params={ - # "source_host": host, - # "target_host": host, - # "blocked_host": IP("213.47.23.195") - # } - # ) - # ) - # # agent 1 exfiltrate data to C&C - # obs1 = agent1.make_step(Action( - # ActionType.ExfiltrateData, - # params={ - # "source_host": host, - # "target_host": IP("213.47.23.195"), - # "data": Data("User1","DataFromServer1") - # } - # ) - # ) - - # print(obs1) - # print(obs2) - - # ####################################### - # 1 ATTACKER - # ####################################### - - #register both agents - agent1 = BaseAgent(args.host, args.port, role="Attacker") - obs1 = agent1.register() - print(obs1) - - # network scans - both agents - obs1 = agent1.make_step(Action( - ActionType.ScanNetwork, - params={ - "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - "target_network":Network("192.168.1.0", 24) - } - ) - ) - - # service scans - both agents - obs1 = agent1.make_step(Action( - ActionType.FindServices, - params={ - "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - "target_host":IP("192.168.1.2") - } - ) - ) - - # agent 1 exploit - host, services = [(k,v) for k,v in obs1.state.known_services.items()][0] - obs1 = agent1.make_step(Action( - ActionType.ExploitService, - params={ - "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - "target_host":host, - "target_service":list(services)[0] - } - ) - ) - # agent 1 find data - obs1 = agent1.make_step(Action( - ActionType.FindData, - params={ - "source_host": list(filter(lambda x: x != IP("213.47.23.195"), obs1.state.controlled_hosts))[0], - "target_host":host, - } - ) - ) - - # agent 1 exfiltrate data to C&C - obs1 = agent1.make_step(Action( - ActionType.ExfiltrateData, - params={ - "source_host": host, - "target_host": IP("213.47.23.195"), - "data": Data("User1","DataFromServer1") - } - ) - ) - - print(obs1) - - - - - - obs1 = agent1.request_game_reset() - #obs2 = agent2.request_game_reset() - print("------------------") - print(obs1) - #print(obs2) - # #obs = agent.request_game_reset() - # for ip in obs.state.controlled_hosts: - # if ip != IP("213.47.23.195"): - # src = ip - # break - # agent.make_step(Action(ActionType.ScanNetwork, params={"source_host":src, "target_network":Network("192.168.1.0", 24)})) - # obs = agent.make_step(Action(ActionType.FindServices, params={"source_host":src, "target_host":IP("192.168.1.2")})) - # host, services = [(k,v) for k,v in obs.state.known_services.items()][0] - # service = list(services)[0] - # agent.make_step(Action(ActionType.ExploitService, params={"source_host":src, "target_host":host, "target_service":service})) - # agent.make_step(Action(action_type=ActionType.FindData, params={"source_host":host, "target_host":host})) - # obs = agent.make_step(Action(action_type=ActionType.ExfiltrateData, params={ - # "source_host":host, - # "target_host":IP("213.47.23.195"), - # "data": Data("User1","DataFromServer1") - # } - # )) - # print(obs) - # # print("----------------------------") - # # obs = agent.request_game_reset() - # # print('---------------------------') - # # obs = agent.make_step(Action(action_type=ActionType.ExfiltrateData, params={ - # # "source_host":host, - # # "target_host":IP("213.47.23.195"), - # # "data": Data("User1","DataFromServer1") - # # } - # # )) - # # print(obs) - diff --git a/agents/benign/random/benign_random_agent.py b/agents/benign/random/benign_random_agent.py index e09d021..6b8cc64 100644 --- a/agents/benign/random/benign_random_agent.py +++ b/agents/benign/random/benign_random_agent.py @@ -8,13 +8,10 @@ from random import uniform import numpy as np import time -#from torch.utils.tensorboard import SummaryWriter -# This is used so the agent can see the environment and game components -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__) ) ) ))) -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__) ))) -# with the path fixed, we can import now -from env.game_components import Action, Observation, ActionType +from AIDojoCoordinator.game_components import Action, Observation, ActionType +# importing agent utils and base agent +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__) )))) from base_agent import BaseAgent from agent_utils import generate_valid_actions @@ -69,7 +66,7 @@ def select_action(self, observation:Observation)->Action: valid_actions = generate_valid_actions(observation.state) # filter actions based on the allowed action types allowed_actions = filter(lambda action: action.type in self._allowed_actions, valid_actions) - allowed_actions = [a for a in allowed_actions] + [Action(ActionType.ResetGame, params={})] + allowed_actions = [a for a in allowed_actions] + [Action(ActionType.ResetGame, parameters={})] action = choice([a for a in allowed_actions]) return action diff --git a/agents/defenders/probabilistic/probabilistic_agent.py b/agents/defenders/probabilistic/probabilistic_agent.py index 5f6994a..6279389 100644 --- a/agents/defenders/probabilistic/probabilistic_agent.py +++ b/agents/defenders/probabilistic/probabilistic_agent.py @@ -4,17 +4,19 @@ import sys import logging import os -from random import choice import argparse -from random import uniform import numpy as np import time - +from random import choice +from random import uniform # This is used so the agent can see the environment and game components -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__) ) ) ))) -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__) ))) +sys.path.append( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) +) # with the path fixed, we can import now -from env.game_components import Action, Observation, ActionType +from AIDojoCoordinator.game_components import Action, Observation, ActionType +# importing agent utils and base agent +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__) )))) from base_agent import BaseAgent from agent_utils import generate_valid_actions @@ -67,7 +69,7 @@ def select_action(self, observation:Observation)->Action: valid_actions = generate_valid_actions(observation.state) # filter actions based on the allowed action types allowed_actions = filter(lambda action: action.type in self._allowed_actions, valid_actions) - allowed_actions = [a for a in allowed_actions] + [Action(ActionType.ResetGame, params={})] + allowed_actions = [a for a in allowed_actions] + [Action(ActionType.ResetGame, parameters={})] action = choice([a for a in allowed_actions]) return action diff --git a/agents/defenders/random/random_agent.py b/agents/defenders/random/random_agent.py index 5e7b515..30a3ea3 100644 --- a/agents/defenders/random/random_agent.py +++ b/agents/defenders/random/random_agent.py @@ -9,12 +9,13 @@ import numpy as np import time import mlflow +from os import path -# This is used so the agent can see the environment and game components -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__) ) ) )))) -sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__) )))) + +# importing agent utils and base agent +sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__) )))) # with the path fixed, we can import now -from env.game_components import Action, Observation, ActionType +from AIDojoCoordinator.game_components import Action, Observation, ActionType from base_agent import BaseAgent from agent_utils import generate_valid_actions @@ -71,7 +72,7 @@ def select_action(self, observation:Observation)->Action: valid_actions = generate_valid_actions(observation.state) # filter actions based on the allowed action types allowed_actions = filter(lambda action: action.type in self._allowed_actions, valid_actions) - allowed_actions = [a for a in allowed_actions] + [Action(ActionType.ResetGame, params={})] + allowed_actions = [a for a in allowed_actions] + [Action(ActionType.ResetGame, parameters={})] action = choice([a for a in allowed_actions]) return action diff --git a/agents/graph_agent_utils.py b/agents/graph_agent_utils.py index 3c25881..2b8ad16 100644 --- a/agents/graph_agent_utils.py +++ b/agents/graph_agent_utils.py @@ -12,7 +12,7 @@ sys.path.append(path.dirname(path.dirname(path.dirname(path.abspath(__file__) )))) #with the path fixed, we can import now -from env.game_components import GameState, IP, Network, Data, Service +from AIDojoCoordinator.game_components import GameState, IP, Network, Data, Service def state_as_graph(state:GameState) -> tuple: node_types = { diff --git a/agents/llm_utils.py b/agents/llm_utils.py index a5ac76b..408abd5 100644 --- a/agents/llm_utils.py +++ b/agents/llm_utils.py @@ -8,7 +8,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # with the path fixed, we can import now -from env.game_components import ( +from AIDojoCoordinator.game_components import ( ActionType, Action, IP,