diff --git a/docs/Makefile b/docs/Makefile index 99e19d16..d0f9af01 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,4 +1,4 @@ -# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK # Minimal makefile for Sphinx documentation # You can set these variables from the command line, and also # from the environment for the first two. diff --git a/docs/build-sphinx-docs-to-github-pages.sh b/docs/build-sphinx-docs-to-github-pages.sh index 9e43a2b4..a180f168 100644 --- a/docs/build-sphinx-docs-to-github-pages.sh +++ b/docs/build-sphinx-docs-to-github-pages.sh @@ -1,4 +1,4 @@ -# © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK #!/bin/bash set -x diff --git a/src/primaite/config/_package_data/uc7_config.yaml b/src/primaite/config/_package_data/uc7_config.yaml new file mode 100644 index 00000000..f5d4ac54 --- /dev/null +++ b/src/primaite/config/_package_data/uc7_config.yaml @@ -0,0 +1,2677 @@ +########################################################## +# USE CASE 7 CONFIGURATION YAML FILE # +########################################################## + + +########################################## +# PrimAITE Game and Logging Settings # +########################################## + + +# PrimAITE I/O Settings # + +io_settings: + save_agent_actions: false + save_step_metadata: false + save_pcap_logs: false + save_sys_logs: false + save_agent_logs: false + write_sys_log_to_terminal: false + + +# PrimAITE Game Settings # + +game: + max_episode_length: 128 + ports: + - FTP + - DNS + - HTTP + - NTP + - POSTGRES_SERVER + - SSH + protocols: + - ICMP + - TCP + - UDP + thresholds: + nmne: + high: 10 + medium: 5 + low: 0 + +############################################ +# PrimAITE Use Case 7 Simulation # +############################################ + +########################################## +# Configuration Variables (Yaml Anchors) # +########################################## + +# External Network Address List # + +DNS_SUBNET: &DNS_SUBNET 255.255.255.240 # | 8.8.8.0 / 28 +HOME_INTERNET_SUBNET: &HOME_INTERNET_SUBNET 255.255.255.252 # | 10.1.0.0 / 30 +REMOTE_INTERNET_SUBNET: &REMOTE_INTERNET_SUBNET 255.255.255.252 # | 10.1.10.0 / 30 +SOME_TECH_INTERNET_SUBNET: &ST_INTERNET_SUBNET 255.255.255.252 # | 10.1.100.0 / 30 +HOME_OFFICE_SUBNET: &HOME_SUBNET 255.255.255.0 # | 192.168.1.0 / 26 +REMOTE_SUBNET_DMZ: &REMOTE_SUBNET_DMZ 255.255.255.252 # | 192.168.10.0 / 30 +REMOTE_SUBNET: &REMOTE_SUBNET 255.255.255.240 # | 192.168.20.0 / 28 + +# SOME_TECH (ST) Network Address List # + +SOME_TECH_DMZ_SUBNET: &ST_DMZ_SUBNET 255.255.255.252 # | 192.168.100.0 / 30 +SOME_TECH_INTRANET_RT_CR_SUBNET: &ST_INTRA_CR_SUBNET 255.255.255.240 # | 192.168.150.0 / 28 +SOME_TECH_INTRANET_RT_DR_ONE_SUBNET: &ST_INTRA_DR_ONE_SUBNET 255.255.255.252 # | 192.168.160.0 / 30 +SOME_TECH_INTRANET_RT_DR_TWO_SUBNET: &ST_INTRA_DR_TWO_SUBNET 255.255.255.252 # | 192.168.170.0 / 30 +SOME_TECH_HEAD_OFFICE_SUBNET: &ST_HO_SUBNET 255.255.255.248 # | 192.168.200.0 / 29 +SOME_TECH_HUMAN_RESOURCES_SUBNET: &ST_HR_SUBNET 255.255.255.248 # | 192.168.210.0 / 29 +SOME_TECH_DATA_SUBNET: &ST_DATA_SUBNET 255.255.255.248 # | 192.168.220.0 / 29 +SOME_TECH_PROJECT_A_SUBNET: &ST_PROJ_A_SUBNET 255.255.255.248 # | 192.168.230.0 / 29 +SOME_TECH_PROJECT_B_SUBNET: &ST_PROJ_B_SUBNET 255.255.255.248 # | 192.168.240.0 / 29 +SOME_TECH_PROJECT_C_SUBNET: &ST_PROJ_C_SUBNET 255.255.255.248 # | 192.168.250.0 / 29 + +# Host & Server Configurations # + +# ST Public Web Server | web-server | ST_DMZ-PUB-SRV-WEB +SOME_TECH_PUBLIC_SERVER_WEB_IP_ADDRESS: &ST_PUB_SRV_WEB_IP 192.168.100.2 +SOME_TECH_PUBLIC_SERVER_WEB_CONFIG: &ST_SRV_WEB_CONFIG # + - type: web-server + +# ISP Public DNS | dns-server | ISP-PUB-SRV-DNS +PUBLIC_DNS_IP_ADDRESS: &PUBLIC_DNS_IP 8.8.8.8 +PUBLIC_DNS_CONFIG: &PUBLIC_DNS_CONFIG # + - type: dns-server + options: + domain_mapping: + some_tech.com: *ST_PUB_SRV_WEB_IP + +# ST Private Storage Server | ftp-server | ST_DATA-PRV-SRV-STORAGE +SOME_TECH_PRIVATE_SERVER_STORAGE_IP: &ST_SRV_STORAGE_IP 192.168.220.2 +SOME_TECH_PRIVATE_SERVER_STORAGE_CONFIG: &ST_SRV_STORAGE_CONFIG + - type: ftp-server + +# ST Private Database Server | database-client & ftp-client | ST_DATA-PRV-SRV-DB +SOME_TECH_PRIVATE_SERVER_DATABASE_IP: &ST_SRV_DB_IP 192.168.220.3 +SOME_TECH_PRIVATE_SERVER_DATABASE_CONFIG: &ST_SRV_DB_CONFIG + - type: database-service + options: + backup_server_ip: *ST_SRV_STORAGE_IP + - type: ftp-client + +# Default PC Configuration | Database Client & Web Server +PERSONAL_COMPUTER_DEFAULT_CONFIG: &PC_DEFAULT_CONFIG + - type: database-client + options: + db_server_ip: *ST_SRV_DB_IP + - type: web-browser + options: + target_url: http://some_tech.com + + +############################## +# Simulation Configuration # +############################## + +simulation: + defaults: + folder_scan_duration: 0 + folder_restore_duration: 3 + service_fix_duration: 2 + service_restart_duration: 2 + software_install_duration: 0 + node_start_up_duration: 3 + node_shut_down_duration: 3 + node_scan_duration: 8 + network: + nmne_config: + capture_nmne: true + nmne_capture_keywords: + - DELETE + - ENCRYPT + nodes: + ###################### + # HOME OFFICE SUBNET # + ###################### + - hostname: HOME-PUB-RT-DR + type: router + default_gateway: 10.1.0.1 + ports: + 1: + ip_address: 192.168.1.1 + subnet_mask: *HOME_SUBNET + 2: + ip_address: 10.1.0.2 + subnet_mask: *HOME_INTERNET_SUBNET + default_route: + next_hop_ip_address: 10.1.0.1 + acl: + 5: + action: PERMIT + + - hostname: HOME-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: HOME-PUB-PC-1 + type: computer + ip_address: 192.168.1.2 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-PC-2 + type: computer + ip_address: 192.168.1.3 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-SRV + type: server + ip_address: 192.168.1.4 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + + ###################### + # INTERNET SUBNET # + ###################### + - hostname: ISP-PUB-RT-BR + type: router + ports: + 1: + ip_address: 10.1.0.1 + subnet_mask: *HOME_INTERNET_SUBNET + 2: + ip_address: 8.8.8.1 + subnet_mask: *DNS_SUBNET + 3: + ip_address: 10.1.10.1 + subnet_mask: *REMOTE_INTERNET_SUBNET + 4: + ip_address: 10.1.100.1 + subnet_mask: *ST_INTERNET_SUBNET + routes: + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.0.2 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 8.8.8.8 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.10.2 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.10.2 + + default_route: + next_hop_ip_address: 10.1.100.2 # SOME_TECH Firewall + + acl: + 5: + action: PERMIT + + ################ + # DNS SUBNET # + ################ + - hostname: ISP-PUB-SRV-DNS + type: server + ip_address: 8.8.8.8 + subnet_mask: *DNS_SUBNET + default_gateway: 8.8.8.1 + services: + *PUBLIC_DNS_CONFIG + applications: + - type: c2-server # Represents the external internet. + options: + listen_on_ports: + - 80 + - 53 + - 21 + ######################## + # REMOTE SITE SUBNET # + ######################## + - hostname: REM-PUB-FW + type: firewall + ports: + external_port: # Public Internet facing + ip_address: 10.1.10.2 + subnet_mask: *REMOTE_INTERNET_SUBNET + internal_port: # Remote Site (DMZ Subnet) facing + ip_address: 192.168.10.1 + subnet_mask: *REMOTE_SUBNET_DMZ + routes: + - address: 192.168.20.0 # Remote Site Network + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 192.168.10.2 + default_route: + next_hop_ip_address: 10.1.10.1 # Forward to internet router port 3 + acl: + internal_inbound_acl: + 1: + action: PERMIT + internal_outbound_acl: + 1: + action: PERMIT + dmz_inbound_acl: + 1: + action: PERMIT + dmz_outbound_acl: + 1: + action: PERMIT + external_inbound_acl: + 1: + action: PERMIT + external_outbound_acl: + 1: + action: PERMIT + + - hostname: REM-PUB-RT-DR + type: router + default_gateway: 192.168.10.1 + ports: + 1: + ip_address: 192.168.10.2 + subnet_mask: *REMOTE_SUBNET_DMZ + 2: + ip_address: 192.168.20.1 + subnet_mask: *REMOTE_SUBNET + default_route: + next_hop_ip_address: 192.168.10.1 + acl: + 5: + action: PERMIT + + - hostname: REM-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: REM-PUB-PC-1 + type: computer + ip_address: 192.168.20.2 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-PC-2 + type: computer + ip_address: 192.168.20.3 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-SRV + type: server + ip_address: 192.168.20.4 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + + + ######################## + # SOME_TECH DMZ SUBNET # + ######################## + + - hostname: ST_PUB-FW + type: firewall + ports: + external_port: # Public Internet Facing Port + ip_address: 10.1.100.2 + subnet_mask: *ST_INTERNET_SUBNET + internal_port: # SOME_TECH Intranet Port + ip_address: 192.168.150.1 + subnet_mask: *ST_INTRA_CR_SUBNET + dmz_port: # SOME_TECH Port Facing Port + ip_address: 192.168.100.1 + subnet_mask: *ST_DMZ_SUBNET + acl: + internal_inbound_acl: + 5: + action: PERMIT + internal_outbound_acl: + 5: + action: PERMIT + dmz_inbound_acl: + 5: + action: PERMIT + dmz_outbound_acl: + 5: + action: PERMIT + external_inbound_acl: + 5: + action: PERMIT + external_outbound_acl: + 5: + action: PERMIT + routes: + - address: 10.1.100.0 + subnet_mask: *ST_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.10.0 + subnet_mask: *REMOTE_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.0.0 + subnet_mask: *HOME_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 10.1.100.1 + + default_route: + next_hop_ip_address: 192.168.150.2 + + - hostname: ST_DMZ-PUB-SRV-WEB + type: server + ip_address: *ST_PUB_SRV_WEB_IP + subnet_mask: *ST_DMZ_SUBNET + default_gateway: 192.168.100.1 + services: + *ST_SRV_WEB_CONFIG + + ############################# + # SOME_TECH INTRANET SUBNET # + ############################# + + - hostname: ST_INTRA-PRV-RT-CR + type: router + ports: + 1: + ip_address: 192.168.150.2 + subnet_mask: *ST_INTRA_CR_SUBNET + 2: + ip_address: 192.168.160.1 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 3: + ip_address: 192.168.170.1 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 4: + ip_address: 192.168.220.1 + subnet_mask: *ST_DATA_SUBNET + + routes: + - address: 192.168.200.0 + subnet_mask: *ST_HO_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.210.0 + subnet_mask: *ST_HR_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.230.0 + subnet_mask: *ST_PROJ_A_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.240.0 + subnet_mask: *ST_PROJ_B_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.250.0 + subnet_mask: *ST_PROJ_C_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + + default_route: + next_hop_ip_address: 192.168.150.1 # ST Public Firewall Internal Port + acl: + 5: + action: PERMIT + + - hostname: ST_INTRA-PRV-RT-DR-1 + type: router + ports: + 1: + ip_address: 192.168.160.2 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 2: + ip_address: 192.168.230.1 + subnet_mask: *ST_PROJ_A_SUBNET + 3: + ip_address: 192.168.240.1 + subnet_mask: *ST_PROJ_B_SUBNET + 4: + ip_address: 192.168.250.1 + subnet_mask: *ST_PROJ_C_SUBNET + default_route: + next_hop_ip_address: 192.168.160.1 # ST Intranet CR Router Port 2 + acl: + 5: + action: PERMIT + + - hostname: ST_INTRA-PRV-RT-DR-2 + type: router + default_gateway: 192.168.170.1 + ports: + 1: + ip_address: 192.168.170.2 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 2: + ip_address: 192.168.200.1 + subnet_mask: *ST_HO_SUBNET + 3: + ip_address: 192.168.210.1 + subnet_mask: *ST_HR_SUBNET + default_route: + next_hop_ip_address: 192.168.170.1 # ST Intranet CR Router Port 3 + acl: + 5: + action: PERMIT + + ################################ + # SOME_TECH HEAD OFFICE SUBNET # + ################################ + + - hostname: ST_HO-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_HO-PRV-PC-1 + type: computer + ip_address: 192.168.200.2 + default_gateway: 192.168.200.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HO-PRV-PC-2 + type: computer + ip_address: 192.168.200.3 + default_gateway: 192.168.200.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HO-PRV-PC-3 + type: computer + ip_address: 192.168.200.4 + default_gateway: 192.168.200.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + #################################### + # SOME_TECH HUMAN RESOURCES SUBNET # + #################################### + + - hostname: ST_HR-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_HR-PRV-PC-1 + type: computer + ip_address: 192.168.210.2 + default_gateway: 192.168.210.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HR-PRV-PC-2 + type: computer + ip_address: 192.168.210.3 + default_gateway: 192.168.210.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HR-PRV-PC-3 + type: computer + ip_address: 192.168.210.4 + default_gateway: 192.168.210.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ######################### + # SOME_TECH DATA SUBNET # + ######################### + + - hostname: ST_DATA-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_DATA-PRV-SRV-STORAGE + type: server + ip_address: *ST_SRV_STORAGE_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_STORAGE_CONFIG + + - hostname: ST_DATA-PRV-SRV-DB + type: server + ip_address: *ST_SRV_DB_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_DB_CONFIG + + ####################### + # SOME_TECH PROJECT A # + ####################### + + - hostname: ST_PROJ-A-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_PROJ-A-PRV-PC-1 + type: computer + ip_address: 192.168.230.2 + default_gateway: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-A-PRV-PC-2 + type: computer + ip_address: 192.168.230.3 + default_gateway: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-A-PRV-PC-3 + type: computer + ip_address: 192.168.230.4 + default_gateway: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT B # + ####################### + + - hostname: ST_PROJ-B-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_PROJ-B-PRV-PC-1 + type: computer + ip_address: 192.168.240.2 + default_gateway: 192.168.240.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-B-PRV-PC-2 + type: computer + ip_address: 192.168.240.3 + default_gateway: 192.168.240.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-B-PRV-PC-3 + type: computer + ip_address: 192.168.240.4 + default_gateway: 192.168.240.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT C # + ####################### + + - hostname: ST_PROJ-C-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_PROJ-C-PRV-PC-1 + type: computer + ip_address: 192.168.250.2 + default_gateway: 192.168.250.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-C-PRV-PC-2 + type: computer + ip_address: 192.168.250.3 + default_gateway: 192.168.250.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-C-PRV-PC-3 + type: computer + ip_address: 192.168.250.4 + default_gateway: 192.168.250.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + + ############################## + # Simulation Network Links # + ############################## + links: + + ############################ + # HOME OFFICE SUBNET LINKS # + ############################ + + # Home Switch (Port 1) --> Home Router (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 1 + + # Home Switch (Port 2) --> PC 1 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: HOME-PUB-PC-1 + endpoint_b_port: 1 + + # Home Switch (Port 3) --> PC 2 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: HOME-PUB-PC-2 + endpoint_b_port: 1 + + # Home Switch (Port 4) --> PC SRV (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: HOME-PUB-SRV + endpoint_b_port: 1 + + ################## + # Internet Links # + ################## + + # Internet Router (Port 1) --> Home Router (Port 2) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 2 + + # Internet Router (Port 2) --> DNS Server (Port 1) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 2 + endpoint_b_hostname: ISP-PUB-SRV-DNS + endpoint_b_port: 1 + + # Internet Router (Port 3) --> Remote Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-FW + endpoint_b_port: 1 + + #################### + # Remote DMZ Links # + #################### + + # Remote Firewall (Internal Port) --> Remote Site Router (Port 1) + - endpoint_a_hostname: REM-PUB-FW + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-RT-DR + endpoint_b_port: 1 + + #################### + # Remote Site Link # + #################### + + # Remote Site Router (Port 2) --> Remote Site Switch (Port 1) + - endpoint_a_hostname: REM-PUB-RT-DR + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-SW-AS + endpoint_b_port: 1 + + # Remote Site Switch (Port 2) --> Remote Site PC 1 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-PC-1 + endpoint_b_port: 1 + + # Remote Site Switch (Port 3) --> Remote Site PC 2 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-PC-2 + endpoint_b_port: 1 + + # Remote Site Switch (Port 4) --> Remote Site Server (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: REM-PUB-SRV + endpoint_b_port: 1 + + ####################### + # SOME_TECH DMZ Links # + ####################### + + # Internet Router (Port 4) --> Some Tech DMZ Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 4 + endpoint_b_hostname: ST_PUB-FW + endpoint_b_port: 1 + + # Some Tech DMZ Firewall (DMZ Port) --> Some Tech Web Server (Port 1) + - endpoint_a_hostname: ST_PUB-FW + endpoint_a_port: 3 + endpoint_b_hostname: ST_DMZ-PUB-SRV-WEB + endpoint_b_port: 1 + + ############################ + # SOME_TECH INTRANET Links # + ############################ + + # Some Tech Intranet CR Router (Port 1) --> Some Tech DMZ Firewall (Internal Port) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 1 + endpoint_b_hostname: ST_PUB-FW + endpoint_b_port: 2 + + # Some Tech Intranet CR Router (Port 2) --> Some Tech Intranet DR Router 1 (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 2 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_b_port: 1 + + # Some Tech Intranet CR Router (Port 3) --> Some Tech Intranet DR Router 2 (Port 2) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 3 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-2 + endpoint_b_port: 1 + + # Some Tech Intranet Private Router CR (Port 4) --> Some Tech Data Private Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 4 + endpoint_b_hostname: ST_DATA-PRV-SW-AS + endpoint_b_port: 1 + + + ############################### + # SOME_TECH HEAD OFFICE Links # + ############################### + + # Some Tech Head Office Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 2) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-2 + endpoint_b_port: 2 + + # Some Tech Head Office Switch (Port 2) --> Some Tech Head Office PC 1 (Port 1) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_HO-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 3) --> Some Tech Head Office PC 2 (Port 1) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_HO-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 4) --> Some Tech Head Office PC 3 (Port 1) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_HO-PRV-PC-3 + endpoint_b_port: 1 + + + ################################### + # SOME_TECH HUMAN RESOURCES Links # + ################################### + + # Some Tech Human Resources Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 3) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-2 + endpoint_b_port: 3 + + # Some Tech Human Resources Switch (Port 2) --> Some Tech Human Resources PC 1 (Port 1) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_HR-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 3) --> Some Tech Human Resources PC 2 (Port 1) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_HR-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 4) --> Some Tech Human Resources PC 3 (Port 1) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_HR-PRV-PC-3 + endpoint_b_port: 1 + + ######################## + # SOME_TECH DATA Links # + ######################## + + # Some Tech Data Switch (Port 2) --> Some Tech Data Private Storage Server (Port 1) + - endpoint_a_hostname: ST_DATA-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_DATA-PRV-SRV-STORAGE + endpoint_b_port: 1 + + # Some Tech Data Switch (Port 3) --> Some Tech Data Private Database Server (Port 1) + + - endpoint_a_hostname: ST_DATA-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_DATA-PRV-SRV-DB + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT A Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 2) --> Some Tech Private Project A Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 2) --> Some Tech Project A PC 1 + - endpoint_a_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-A-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 3) --> Some Tech Project A PC 2 + - endpoint_a_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-A-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 4) --> Some Tech Project A PC 3 + - endpoint_a_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-A-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT B Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 3) --> Some Tech Private Project B Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 2) --> Some Tech Project B PC 1 + - endpoint_a_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-B-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 3) --> Some Tech Project B PC 2 + - endpoint_a_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-B-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 4) --> Some Tech Project B PC 3 + - endpoint_a_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-B-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT C Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 4) --> Some Tech Private Project C Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 2) --> Some Tech Project C PC 1 + - endpoint_a_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-C-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 3) --> Some Tech Project C PC 2 + - endpoint_a_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-C-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 4) --> Some Tech Project C PC 3 + - endpoint_a_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-C-PRV-PC-3 + endpoint_b_port: 1 + +################################## +# Use Case 7 Agent YAML Anchors # +################################## + +############################## +# Green Agent YAML Anchors # +############################## + +# Green Agent Reward Impacts Values # + +LOW_WEIGHT_IMPACT: &LOW_WEIGHT_IMPACT 0.2 +MEDIUM_WEIGHT_IMPACT: &MEDIUM_WEIGHT_IMPACT 0.5 +HIGH_WEIGHT_IMPACT: &HIGH_WEIGHT_IMPACT 0.95 + +LOW_WEIGHT_IMPACT_NEGATIVE: &LOW_WEIGHT_IMPACT_NEG -0.2 +MEDIUM_WEIGHT_IMPACT_NEGATIVE: &MEDIUM_WEIGHT_IMPACT_NEG -0.5 +HIGH_WEIGHT_IMPACT_NEGATIVE: &HIGH_WEIGHT_IMPACT_NEG -0.8 + +# Default Green Agent Action Space Configuration Anchor # + +DEFAULT_GREEN_AGENT_MAX_EXECUTIONS: &DEFAULT_GREEN_AGENT_MAX_EXECUTIONS 1000 # Ensures green agent activity through-out an episode + +################################################# +# Probabilistic Green Agent Config Yaml Anchors # +################################################# + +# Probabilistic Green Agent | 20% node-application-execute | 80% do-nothing # +PROBABILISTIC_CONFIG_20_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_20 + action_probabilities: + 0: 0.8 + 1: 0.2 + +# Probabilistic Green Agent | 40% node-application-execute | 60% do-nothing # +PROBABILISTIC_CONFIG_40_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_40 + action_probabilities: + 0: 0.6 + 1: 0.4 + +# Probabilistic Green Agent | 60% node-application-execute | 40% do-nothing # +PROBABILISTIC_CONFIG_60_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_60 + action_probabilities: + 0: 0.4 + 1: 0.6 + + +# System Green Agent Config UC7 Network Wide Yaml Anchor # + +# Lists the IP_Address of all hosts that contain DNS and NTP Service Clients +UC7_IP_LIST: &UC7_IP_LIST + # ====== Home Office ======== + - 192.168.1.2 # HOME-PUB-PC-1 | ip_id: 2 + - 192.168.1.3 # HOME-PUB-PC-2 | ip_id: 3 + - 192.168.1.4 # HOME-PUB-PC-SRV | ip_id: 4 + # ====== Remote Site ======== + - 192.168.20.2 # REM-PUB-PC-1 | ip_id: 5 + - 192.168.20.3 # REM-PUB-PC-2 | ip_id: 6 + - 192.168.20.4 # REM-PUB-SRV | ip_id: 7 + # ====== ST Public DMZ ======= + - *ST_PUB_SRV_WEB_IP # 192.168.100.2 (ST_DMZ-PUB-SRV-WEB) | ip_id: 8 + # ====== ST Head Office ======= + - 192.168.200.2 # ST_HO-PRV-PC-1 | ip_id: 9 + - 192.168.200.3 # ST_HO-PRV-PC-2 | ip_id: 10 + - 192.168.200.4 # ST_HO-PRV-PC-3 | ip_id: 11 + # ===== ST Human Resources ====== + - 192.168.210.2 # ST_HR-PRV-PC-1 | ip_id: 12 + - 192.168.210.3 # ST_HR-PRV-PC-2 | ip_id: 13 + - 192.168.210.4 # ST_HR-PRV-PC-3 | ip_id: 14 + # ====== ST DATA Servers ======= + - *ST_SRV_STORAGE_IP # 192.168.220.2 (ST_DATA-PRV-SRV-STORAGE) | ip_id: 15 + - *ST_SRV_DB_IP # 192.168.220.3 (ST_DATA-PRV-SRV-DB) | ip_id: 16 + # ====== ST Project A ======= + - 192.168.230.2 # PROJ-A-PRV-PC-1 | ip_id: 17 + - 192.168.230.3 # PROJ-A-PRV-PC-2 | ip_id: 18 + - 192.168.230.4 # PROJ-A-PRV-PC-3 | ip_id: 19 + # ====== ST Project B ======= + - 192.168.240.2 # PROJ-B-PRV-PC-1 | ip_id: 20 + - 192.168.240.3 # PROJ-B-PRV-PC-2 | ip_id: 21 + - 192.168.240.4 # PROJ-B-PRV-PC-3 | ip_id: 22 + # ====== ST Project C ======= + - 192.168.250.2 # PROJ-C-PRV-PC-1 | ip_id: 23 + - 192.168.250.3 # PROJ-C-PRV-PC-2 | ip_id: 24 + - 192.168.250.4 # PROJ-C-PRV-PC-3 | ip_id: 25 + +############################################ +# Use Case 7 Agent Configuration Section # +############################################ + +agents: + ####################################################### + # UC7 Green Agents Path of Life (POL) Configuration # + ####################################################### + + + #################################### + # Home Office Network POL Config # + #################################### + + # Home Office Green Agent Pattern Of Life + # ====================================== + # 1. Three Home workers accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. Three Home workers accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: HOME_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-1"] + target_application: "database-client" + start_step: 4 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-2"] + target_application: "database-client" + start_step: 8 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + - ref: HOME_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + #################################### + # Remote Site Network POL Config # + #################################### + + # Remote Site Green Agent Pattern Of Life + # ====================================== + # 1. Three Remote workers accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. Three Remote workers accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: REMOTE_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-1"] + target_application: "database-client" + start_step: 12 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-2"] + target_application: "database-client" + start_step: 16 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + - ref: REMOTE_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + #################################### + # ST Project A Network POL Config # + #################################### + + # ST Project A Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + + - ref: PROJ_A-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-A-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-1 + + - ref: PROJ_A-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-1 + + - ref: PROJ_A-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-A-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-A-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-A-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-3 + + - ref: PROJ_A-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-A-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-3 + + #################################### + # ST Project B Network POL Config # + #################################### + + # ST Project B Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: PROJ_B-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-B-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-1 + + - ref: PROJ_B-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-B-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-1 + + - ref: PROJ_B-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-B-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-B-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-3 + + - ref: PROJ_B-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-B-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-3 + + #################################### + # ST Project C Network POL Config # + #################################### + + # ST Project C Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: PROJ_C-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-C-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-1 + + - ref: PROJ_C-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-C-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-1 + + - ref: PROJ_C-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-C-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-C-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-C-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-3 + + - ref: PROJ_C-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-3 + + ###################################### + # ST Head Office Network POL Config # + ###################################### + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. The ST CEO accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 2. The ST CTO accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. The ST CFO accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: CEO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HO-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST_HO-PRV-PC-1 + + - ref: CTO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HO-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HO-PRV-PC-2 + + - ref: CFO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HO-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HO-PRV-PC-3 + + ########################################## + # ST Human Resources Network POL Config # + ########################################## + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. A senior HR staff accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 2. Two junior HR staff accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: SENIOR_HR + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HR-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HR-PRV-PC-1 + + - ref: JUNIOR_HR-1 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HR-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HR-PRV-PC-2 + + - ref: JUNIOR_HR-2 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HR-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HR-PRV-PC-3 + + ########################## + # UC7 Red Agent Config # + ########################## + + - ref: attacker + team: RED + type: tap-001 + agent_settings: + start_step: 1 + frequency: 5 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_target_ip: *ST_SRV_DB_IP + default_starting_node: "ST_PROJ-A-PRV-PC-1" + # starting_nodes: ["ST_PROJ-A-PRV-PC-1", "ST_PROJ-B-PRV-PC-2", "ST_PROJ-C-PRV-PC-3"] + starting_nodes: + kill_chain: + ACTIVATE: + probability: 1 + PROPAGATE: + probability: 1 + scan_attempts: 20 + repeat_scan: false + network_addresses: + - 192.168.230.0/29 # ST Project A + - 192.168.10.0/26 # Remote Site + - 192.168.20.0/30 # Remote DMZ + - 192.168.220.0/29 # ST Data (Contains Target) + COMMAND_AND_CONTROL: + probability: 1 + keep_alive_frequency: 5 + masquerade_port: HTTP + masquerade_protocol: TCP + c2_server_name: ISP-PUB-SRV-DNS + c2_server_ip: *PUBLIC_DNS_IP + PAYLOAD: + probability: 1 + exfiltrate: true + corrupt: true + exfiltration_folder_name: + target_username: admin + target_password: admin + continue_on_failed_exfil: True + + +########################### +# UC7 Blue Agent Config # +########################### + + - ref: defender + team: BLUE + type: proxy-agent + observation_space: + type: custom + options: + components: + - type: nodes + label: NODES + options: + hosts: + # TAP001 Potential Starting Note | ST_PROJ-A-PRV-PC-1 + - hostname: ST_PROJ-A-PRV-PC-1 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST_PROJ-B-PRV-PC-2 + - hostname: ST_PROJ-B-PRV-PC-2 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST_PROJ-C-PRV-PC-3 + - hostname: ST_PROJ-C-PRV-PC-3 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # ST DATA Server Database + - hostname: ST_DATA-PRV-SRV-DB + folders: + - folder_name: database + files: + - file_name: database.db + routers: + - hostname: ST_INTRA-PRV-RT-CR # TAP003 Malicious ACL Target | ROUTER0 + - hostname: ST_INTRA-PRV-RT-DR-1 # TAP003 Malicious ACL Target | ROUTER1 + - hostname: REM-PUB-RT-DR # TAP003 Malicious ACL Target | ROUTER2 + num_ports: 5 + num_services: 2 + num_applications: 2 + num_folders: 2 + num_files: 1 + num_nics: 1 + ip_list: + # ip_list is indexed at 2: + # 0 reserved for padding to align with observations + # 1 reserved for ALL ips + *UC7_IP_LIST + wildcard_list: + - 0.0.0.1 # 0 + - 0.0.0.255 # 1 + - 0.0.255.255 # 2 + port_list: + # 0 is a padding placeholder + # 1 means ALL ports + - FTP # 2 + - DNS # 3 + - HTTP # 4 + - NTP # 5 + - POSTGRES_SERVER # 6 + - SSH # 7 + + protocol_list: + # 0 is padding placeholder + # 1 means ALL protocols + - ICMP # 2 + - TCP # 3 + - UDP # 4 + num_rules: 5 + include_num_access: true + include_nmne: true + monitored_traffic: + tcp: + - HTTP + - POSTGRES_SERVER + icmp: + - NONE + - type: links + label: LINKS + options: + link_references: + # HOME OFFICE SUBNET LINKS + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-RT-DR:eth-1 # 1 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-1:eth-1 # 2 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-2:eth-1 # 3 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-SRV:eth-1 # 4 + # Internet LINKS + - ISP-PUB-RT-BR:eth-1<->HOME-PUB-RT-DR:eth-2 # 5 + - ISP-PUB-RT-BR:eth-2<->ISP-PUB-SRV-DNS:eth-1 # 6 + - ISP-PUB-RT-BR:eth-3<->REM-PUB-FW:eth-1 # 7 + # Remote DMZ Links + - REM-PUB-FW:eth-2<->REM-PUB-RT-DR:eth-1 # 8 + # Remote Site Links + - REM-PUB-RT-DR:eth-2<->REM-PUB-SW-AS:eth-1 # 9 + - REM-PUB-SW-AS:eth-2<->REM-PUB-PC-1:eth-1 # 10 + - REM-PUB-SW-AS:eth-3<->REM-PUB-PC-2:eth-1 # 11 + - REM-PUB-SW-AS:eth-4<->REM-PUB-SRV:eth-1 # 12 + # SOME_TECH DMZ + - ISP-PUB-RT-BR:eth-4<->ST_PUB-FW:eth-1 # 13 + - ST_PUB-FW:eth-3<->ST_DMZ-PUB-SRV-WEB:eth-1 # 14 + # SOME_TECH Intranet + - ST_INTRA-PRV-RT-CR:eth-1<->ST_PUB-FW:eth-2 # 15 + - ST_INTRA-PRV-RT-CR:eth-2<->ST_INTRA-PRV-RT-DR-1:eth-1 # 16 + - ST_INTRA-PRV-RT-CR:eth-3<->ST_INTRA-PRV-RT-DR-2:eth-1 # 17 + - ST_INTRA-PRV-RT-CR:eth-4<->ST_DATA-PRV-SW-AS:eth-1 # 18 + # SOME_TECH Head Office + - ST_HO-PRV-SW-AS:eth-1<->ST_INTRA-PRV-RT-DR-2:eth-2 # 19 + - ST_HO-PRV-SW-AS:eth-2<->ST_HO-PRV-PC-1:eth-1 # 20 + - ST_HO-PRV-SW-AS:eth-3<->ST_HO-PRV-PC-2:eth-1 # 21 + - ST_HO-PRV-SW-AS:eth-4<->ST_HO-PRV-PC-3:eth-1 # 22 + # SOME_TECH Human Resources + - ST_HR-PRV-SW-AS:eth-1<->ST_INTRA-PRV-RT-DR-2:eth-3 # 23 + - ST_HR-PRV-SW-AS:eth-2<->ST_HR-PRV-PC-1:eth-1 # 24 + - ST_HR-PRV-SW-AS:eth-3<->ST_HR-PRV-PC-2:eth-1 # 25 + - ST_HR-PRV-SW-AS:eth-4<->ST_HR-PRV-PC-3:eth-1 # 26 + # SOME_TECH Data Links + - ST_DATA-PRV-SW-AS:eth-2<->ST_DATA-PRV-SRV-STORAGE:eth-1 # 27 + - ST_DATA-PRV-SW-AS:eth-3<->ST_DATA-PRV-SRV-DB:eth-1 # 28 + # SOME_TECH Project A Links + - ST_INTRA-PRV-RT-DR-1:eth-2<->ST_PROJ-A-PRV-SW-AS:eth-1 # 29 + - ST_PROJ-A-PRV-SW-AS:eth2<->ST_PROJ-A-PRV-PC-1:eth-1 # 31 + - ST_PROJ-A-PRV-SW-AS:eth3<->ST_PROJ-A-PRV-PC-2:eth-1 # 32 + - ST_PROJ-A-PRV-SW-AS:eth4<->ST_PROJ-A-PRV-PC-3:eth-1 # 33 + # SOME_TECH Project B Links + - ST_INTRA-PRV-RT-DR-1:eth-3<->ST_PROJ-B-PRV-SW-AS:eth-1 # 34 + - ST_PROJ-B-PRV-SW-AS:eth2<->ST_PROJ-B-PRV-PC-1:eth-1 # 35 + - ST_PROJ-B-PRV-SW-AS:eth3<->ST_PROJ-B-PRV-PC-2:eth-1 # 36 + - ST_PROJ-B-PRV-SW-AS:eth4<->ST_PROJ-B-PRV-PC-3:eth-1 # 37 + # SOME_TECH Project C Links + - ST_INTRA-PRV-RT-DR-1:eth-4<->ST_PROJ-C-PRV-SW-AS:eth-1 # 38 + - ST_PROJ-A-PRV-SW-AS:eth2<->ST_PROJ-C-PRV-PC-1:eth-1 # 39 + - ST_PROJ-A-PRV-SW-AS:eth3<->ST_PROJ-C-PRV-PC-2:eth-1 # 40 + - ST_PROJ-A-PRV-SW-AS:eth4<->ST_PROJ-C-PRV-PC-3:eth-1 # 41 + action_space: + action_map: + 0: + action: do-nothing + options: {} + + # |======================================| + # | ST_PROJ-A-PRV-PC-1 | + # |======================================| + + # ST_PROJ-A-PRV-PC-1 | node-os-scan + 1: + action: node-os-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | node-shutdown + 2: + action: node-shutdown + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | node-startup + 3: + action: node-startup + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | node-reset + 4: + action: node-reset + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | host-nic-disable + 5: + action: host-nic-disable + options: + node_name: ST_PROJ-A-PRV-PC-1 + nic_num: 0 + # ST_PROJ-A-PRV-PC-1 | host-nic-enable + 6: + action: host-nic-enable + options: + node_name: ST_PROJ-A-PRV-PC-1 + nic_num: 0 + # ST_PROJ-A-PRV-PC-1 | node-application-close | database-client + 7: + action: node-application-close + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + # ST_PROJ-A-PRV-PC-1 | node-application-scan | database-client + 8: + action: node-application-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + # ST_PROJ-A-PRV-PC-1 | node-application-fix | database-client + 9: + action: node-application-fix + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + # ST_PROJ-A-PRV-PC-1 | node-application-remove | database-client + 10: + action: node-application-remove + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + + # ST_PROJ-A-PRV-PC-1 | node-file-scan | downloads/malware_dropper.ps1 + 11: + action: node-file-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST_PROJ-A-PRV-PC-1 | node-file-scan | exfiltration_folder/database.db + 12: + action: node-file-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + file_name: database.db + + # ST_PROJ-A-PRV-PC-1 | node-folder-scan | downloads/ + 13: + action: node-folder-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: downloads + + # ST_PROJ-A-PRV-PC-1 | node-folder-scan | exfiltration_folder/ + 14: + action: node-folder-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + + # |======================================| + # | ST_PROJ-B-PRV-PC-2 | + # |======================================| + + # ST_PROJ-B-PRV-PC-2 | node-os-scan + 15: + action: node-os-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | node-shutdown + 16: + action: node-shutdown + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | node-startup + 17: + action: node-startup + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | node-reset + 18: + action: node-reset + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | host-nic-disable + 19: + action: host-nic-disable + options: + node_name: ST_PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST_PROJ-B-PRV-PC-2 | host-nic-enable + 20: + action: host-nic-enable + options: + node_name: ST_PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST_PROJ-B-PRV-PC-2 | node-application-close | database-client + 21: + action: node-application-close + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-application-scan | database-client + 22: + action: node-application-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-application-fix | database-client + 23: + action: node-application-fix + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-application-remove | database-client + 24: + action: node-application-remove + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-file-scan | downloads/malware_dropper.ps1 + 25: + action: node-file-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST_PROJ-B-PRV-PC-2 | node-file-scan | exfiltration_folder/database.db + 26: + action: node-file-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + file_name: database.db + + # ST_PROJ-B-PRV-PC-2 | node-folder-scan | downloads/ + 27: + action: node-folder-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: downloads + + # ST_PROJ-B-PRV-PC-2 | node-folder-scan | exfiltration_folder/ + 28: + action: node-folder-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + + # |======================================| + # | ST_PROJ-C-PRV-PC-3 | + # |======================================| + + # ST_PROJ-C-PRV-PC-3 | node-os-scan + 29: + action: node-os-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | node-shutdown + 30: + action: node-shutdown + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | node-startup + 31: + action: node-startup + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | node-reset + 32: + action: node-reset + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | host-nic-disable + 33: + action: host-nic-disable + options: + node_name: ST_PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST_PROJ-C-PRV-PC-3 | host-nic-enable + 34: + action: host-nic-enable + options: + node_name: ST_PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST_PROJ-C-PRV-PC-3 | node-application-close | database-client + 35: + action: node-application-close + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-application-scan | database-client + 36: + action: node-application-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-application-fix | database-client + 37: + action: node-application-fix + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-application-remove | database-client + 38: + action: node-application-remove + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-file-scan | downloads/malware_dropper.ps1 + 39: + action: node-file-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST_PROJ-C-PRV-PC-3 | node-file-scan | exfiltration_folder/database.db + 40: + action: node-file-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + file_name: database.db + + # ST_PROJ-C-PRV-PC-3 | node-folder-scan | downloads/ + 41: + action: node-folder-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: downloads + + # ST_PROJ-C-PRV-PC-3 | node-folder-scan | exfiltration_folder/ + 42: + action: node-folder-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + + # |======================================| + # | ST_INTRA-PRV-RT-CR | + # |======================================| + + # ST_INTRA-PRV-RT-CR | router-acl-add-rule | P2: ST_PROJ-A-PRV-PC-1 !==> ST_DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 43: + action: router-acl-add-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST_PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST_DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST_INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 1 + 44: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 1 + + # ST_INTRA-PRV-RT-CR | router-acl-add-rule | P3: ST_PROJ-B-PRV-PC-2 !==> ST_DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 45: + action: router-acl-add-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 2 + permission: DENY + src_ip: 192.168.240.3 # (ST_PROJ-B-PRV-PC-2) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST_DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + # ST_INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 2 + 46: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 2 + + # ST_INTRA-PRV-RT-CR | router-acl-add-rule | P4: ST_PROJ-C-PRV-PC-3 !==> ST_DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 47: + action: router-acl-add-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 3 + permission: DENY + src_ip: 192.168.250.4 # (ST_PROJ-C-PRV-PC-3) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST_DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST_INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 3 + 48: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 3 + + # |======================================| + # | ST_DATA-PRV-SRV-DB | + # |======================================| + + # ST_DATA-PRV-SRV-DB | node-file-scan | Scans the database.db file (health status) + 49: + action: node-file-scan + options: + node_name: ST_DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # ST_DATA-PRV-SRV-DB | node-account-change-password | Changes the password of a user account + 50: + action: node-account-change-password + options: + node_name: ST_DATA-PRV-SRV-DB + username: admin # default account + current_password: admin # default password + new_password: thr33_alert_wolv3z # A more 'secure' password + + # |======================================| + # | ST_INTRA-PRV-RT-DR-1 | + # |======================================| + + # ST_INTRA-PRV-RT-DR-1 | router-acl-add-rule | P1: ST_INTRA-PRV-RT-DR-1 !==> ANY (TCP:SSH) + 51: + action: router-acl-add-rule + options: + target_router: ST_INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST_PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: SSH + dst_ip: ALL + dst_wildcard: 0.0.255.255 + dst_port: SSH + protocol_name: TCP + + # ST_INTRA-PRV-RT-DR-1 | node-account-change-password + 52: + action: node-account-change-password + options: + node_name: ST_INTRA-PRV-RT-DR-1 + username: admin + current_password: admin + new_password: secure_password + + # ST_INTRA-PRV-RT-DR-1 | router-acl-remove-rule | Removes the given ACL at position 1 + 53: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-DR-1 + position: 1 + + # |======================================| + # | REM-PUB-RT-DR | + # |======================================| + + # REM-PUB-RT-DR | node-account-change-password + 54: + action: node-account-change-password + options: + node_name: REM-PUB-RT-DR + username: admin + current_password: admin + new_password: secure_password + + # REM-PUB-RT-DR | router-acl-remove-rule | Removes the given ACL at position 1 + 55: + action: router-acl-remove-rule + options: + target_router: REM-PUB-RT-DR + position: 1 + + reward_function: + reward_components: + - type: database-file-integrity + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST_DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # Home Site Green Agents (32 Green Agents each contributing 0.03125 of blue reward) + + # Blue Shared Reward | HOME_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-DB + + # Blue Shared Reward | HOME_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-WEB + + # Blue Shared Reward | HOME_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-DB + + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-WEB + + # Remote Site Green Agents + + # Blue Shared Reward | REMOTE_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-DB + + # Blue Shared Reward | REMOTE_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-WEB + + # Blue Shared Reward | REMOTE_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-DB + + # Blue Shared Reward | REMOTE_WORKER-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-WEB + + # ST Project A Green Agents + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-WEB + + # ST Project B Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Project C Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Head Office Green Agents (CEO/CFO/CTO) + + # Blue Shared Reward | CEO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CEO + + # Blue Shared Reward | CFO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CFO + + # Blue Shared Reward | CTO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CTO + + # ST Human Resources Green Agents + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: SENIOR_HR + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-1 + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-2 + + agent_settings: + flatten_obs: True diff --git a/src/primaite/config/_package_data/uc7_config_tap003.yaml b/src/primaite/config/_package_data/uc7_config_tap003.yaml new file mode 100644 index 00000000..9ac6e1ab --- /dev/null +++ b/src/primaite/config/_package_data/uc7_config_tap003.yaml @@ -0,0 +1,2730 @@ +########################################################## +# USE CASE 7 CONFIGURATION YAML FILE # +########################################################## + + +########################################## +# PrimAITE Game and Logging Settings # +########################################## + + +# PrimAITE I/O Settings # + +io_settings: + save_agent_actions: false + save_step_metadata: false + save_pcap_logs: false + save_sys_logs: false + save_agent_logs: false + # save_sys_logs: true + write_sys_log_to_terminal: false + + +# PrimAITE Game Settings # + +game: + max_episode_length: 128 + ports: + - FTP + - DNS + - HTTP + - NTP + - POSTGRES_SERVER + - SSH + protocols: + - ICMP + - TCP + - UDP + thresholds: + nmne: + high: 10 + medium: 5 + low: 0 + +############################################ +# PrimAITE Use Case 7 Simulation # +############################################ + +########################################## +# Configuration Variables (Yaml Anchors) # +########################################## + +# External Network Address List # + +DNS_SUBNET: &DNS_SUBNET 255.255.255.240 # | 8.8.8.0 / 28 +HOME_INTERNET_SUBNET: &HOME_INTERNET_SUBNET 255.255.255.252 # | 10.1.0.0 / 30 +REMOTE_INTERNET_SUBNET: &REMOTE_INTERNET_SUBNET 255.255.255.252 # | 10.1.10.0 / 30 +SOME_TECH_INTERNET_SUBNET: &ST_INTERNET_SUBNET 255.255.255.252 # | 10.1.100.0 / 30 +HOME_OFFICE_SUBNET: &HOME_SUBNET 255.255.255.0 # | 192.168.1.0 / 26 +REMOTE_SUBNET_DMZ: &REMOTE_SUBNET_DMZ 255.255.255.252 # | 192.168.10.0 / 30 +REMOTE_SUBNET: &REMOTE_SUBNET 255.255.255.240 # | 192.168.20.0 / 28 + +# SOME_TECH (ST) Network Address List # + +SOME_TECH_DMZ_SUBNET: &ST_DMZ_SUBNET 255.255.255.252 # | 192.168.100.0 / 30 +SOME_TECH_INTRANET_RT_CR_SUBNET: &ST_INTRA_CR_SUBNET 255.255.255.240 # | 192.168.150.0 / 28 +SOME_TECH_INTRANET_RT_DR_ONE_SUBNET: &ST_INTRA_DR_ONE_SUBNET 255.255.255.252 # | 192.168.160.0 / 30 +SOME_TECH_INTRANET_RT_DR_TWO_SUBNET: &ST_INTRA_DR_TWO_SUBNET 255.255.255.252 # | 192.168.170.0 / 30 +SOME_TECH_HEAD_OFFICE_SUBNET: &ST_HO_SUBNET 255.255.255.248 # | 192.168.200.0 / 29 +SOME_TECH_HUMAN_RESOURCES_SUBNET: &ST_HR_SUBNET 255.255.255.248 # | 192.168.210.0 / 29 +SOME_TECH_DATA_SUBNET: &ST_DATA_SUBNET 255.255.255.248 # | 192.168.220.0 / 29 +SOME_TECH_PROJECT_A_SUBNET: &ST_PROJ_A_SUBNET 255.255.255.248 # | 192.168.230.0 / 29 +SOME_TECH_PROJECT_B_SUBNET: &ST_PROJ_B_SUBNET 255.255.255.248 # | 192.168.240.0 / 29 +SOME_TECH_PROJECT_C_SUBNET: &ST_PROJ_C_SUBNET 255.255.255.248 # | 192.168.250.0 / 29 + +# Host & Server Configurations # + +# ST Public Web Server | web-server | ST_DMZ-PUB-SRV-WEB +SOME_TECH_PUBLIC_SERVER_WEB_IP_ADDRESS: &ST_PUB_SRV_WEB_IP 192.168.100.2 +SOME_TECH_PUBLIC_SERVER_WEB_CONFIG: &ST_SRV_WEB_CONFIG # + - type: web-server + +# ISP Public DNS | dns-server | ISP-PUB-SRV-DNS +PUBLIC_DNS_IP_ADDRESS: &PUBLIC_DNS_IP 8.8.8.8 +PUBLIC_DNS_CONFIG: &PUBLIC_DNS_CONFIG # + - type: dns-server + options: + domain_mapping: + some_tech.com: *ST_PUB_SRV_WEB_IP + +# ST Private Storage Server | ftp-server | ST_DATA-PRV-SRV-STORAGE +SOME_TECH_PRIVATE_SERVER_STORAGE_IP: &ST_SRV_STORAGE_IP 192.168.220.2 +SOME_TECH_PRIVATE_SERVER_STORAGE_CONFIG: &ST_SRV_STORAGE_CONFIG + - type: ftp-server + +# ST Private Database Server | database-client & ftp-client | ST_DATA-PRV-SRV-DB +SOME_TECH_PRIVATE_SERVER_DATABASE_IP: &ST_SRV_DB_IP 192.168.220.3 +SOME_TECH_PRIVATE_SERVER_DATABASE_CONFIG: &ST_SRV_DB_CONFIG + - type: database-service + options: + backup_server_ip: *ST_SRV_STORAGE_IP + - type: ftp-client + +# Default PC Configuration | Database Client & Web Server +PERSONAL_COMPUTER_DEFAULT_CONFIG: &PC_DEFAULT_CONFIG + - type: database-client + options: + db_server_ip: *ST_SRV_DB_IP + - type: web-browser + options: + target_url: http://some_tech.com + + +############################## +# Simulation Configuration # +############################## + +simulation: + defaults: + folder_scan_duration: 0 + folder_restore_duration: 3 + service_fix_duration: 2 + service_restart_duration: 2 + software_install_duration: 0 + node_start_up_duration: 3 + node_shut_down_duration: 3 + node_scan_duration: 8 + network: + nmne_config: + capture_nmne: true + nmne_capture_keywords: + - DELETE + - ENCRYPT + nodes: + ###################### + # HOME OFFICE SUBNET # + ###################### + - hostname: HOME-PUB-RT-DR + type: router + default_gateway: 10.1.0.1 + ports: + 1: + ip_address: 192.168.1.1 + subnet_mask: *HOME_SUBNET + 2: + ip_address: 10.1.0.2 + subnet_mask: *HOME_INTERNET_SUBNET + default_route: + next_hop_ip_address: 10.1.0.1 + acl: + 5: + action: PERMIT + + - hostname: HOME-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: HOME-PUB-PC-1 + type: computer + ip_address: 192.168.1.2 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-PC-2 + type: computer + ip_address: 192.168.1.3 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-SRV + type: server + ip_address: 192.168.1.4 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + + ###################### + # INTERNET SUBNET # + ###################### + - hostname: ISP-PUB-RT-BR + type: router + ports: + 1: + ip_address: 10.1.0.1 + subnet_mask: *HOME_INTERNET_SUBNET + 2: + ip_address: 8.8.8.1 + subnet_mask: *DNS_SUBNET + 3: + ip_address: 10.1.10.1 + subnet_mask: *REMOTE_INTERNET_SUBNET + 4: + ip_address: 10.1.100.1 + subnet_mask: *ST_INTERNET_SUBNET + routes: + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.0.2 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 8.8.8.8 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.10.2 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.10.2 + + default_route: + next_hop_ip_address: 10.1.100.2 # SOME_TECH Firewall + + acl: + 5: + action: PERMIT + + ################ + # DNS SUBNET # + ################ + - hostname: ISP-PUB-SRV-DNS + type: server + ip_address: 8.8.8.8 + subnet_mask: *DNS_SUBNET + default_gateway: 8.8.8.1 + services: + *PUBLIC_DNS_CONFIG + applications: + - type: c2-server # Represents the external internet. + options: + listen_on_ports: + - 80 + - 53 + - 21 + ######################## + # REMOTE SITE SUBNET # + ######################## + - hostname: REM-PUB-FW + type: firewall + ports: + external_port: # Public Internet facing + ip_address: 10.1.10.2 + subnet_mask: *REMOTE_INTERNET_SUBNET + internal_port: # Remote Site (DMZ Subnet) facing + ip_address: 192.168.10.1 + subnet_mask: *REMOTE_SUBNET_DMZ + routes: + - address: 192.168.20.0 # Remote Site Network + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 192.168.10.2 + default_route: + next_hop_ip_address: 10.1.10.1 # Forward to internet router port 3 + acl: + internal_inbound_acl: + 1: + action: PERMIT + internal_outbound_acl: + 1: + action: PERMIT + dmz_inbound_acl: + 1: + action: PERMIT + dmz_outbound_acl: + 1: + action: PERMIT + external_inbound_acl: + 1: + action: PERMIT + external_outbound_acl: + 1: + action: PERMIT + + - hostname: REM-PUB-RT-DR + type: router + default_gateway: 192.168.10.1 + ports: + 1: + ip_address: 192.168.10.2 + subnet_mask: *REMOTE_SUBNET_DMZ + 2: + ip_address: 192.168.20.1 + subnet_mask: *REMOTE_SUBNET + default_route: + next_hop_ip_address: 192.168.10.1 + acl: + 5: + action: PERMIT + + - hostname: REM-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: REM-PUB-PC-1 + type: computer + ip_address: 192.168.20.2 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-PC-2 + type: computer + ip_address: 192.168.20.3 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-SRV + type: server + ip_address: 192.168.20.4 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + + + ######################## + # SOME_TECH DMZ SUBNET # + ######################## + + - hostname: ST_PUB-FW + type: firewall + ports: + external_port: # Public Internet Facing Port + ip_address: 10.1.100.2 + subnet_mask: *ST_INTERNET_SUBNET + internal_port: # SOME_TECH Intranet Port + ip_address: 192.168.150.1 + subnet_mask: *ST_INTRA_CR_SUBNET + dmz_port: # SOME_TECH Port Facing Port + ip_address: 192.168.100.1 + subnet_mask: *ST_DMZ_SUBNET + acl: + internal_inbound_acl: + 5: + action: PERMIT + internal_outbound_acl: + 5: + action: PERMIT + dmz_inbound_acl: + 5: + action: PERMIT + dmz_outbound_acl: + 5: + action: PERMIT + external_inbound_acl: + 5: + action: PERMIT + external_outbound_acl: + 5: + action: PERMIT + routes: + - address: 10.1.100.0 + subnet_mask: *ST_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.10.0 + subnet_mask: *REMOTE_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.0.0 + subnet_mask: *HOME_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 10.1.100.1 + + default_route: + next_hop_ip_address: 192.168.150.2 + + - hostname: ST_DMZ-PUB-SRV-WEB + type: server + ip_address: *ST_PUB_SRV_WEB_IP + subnet_mask: *ST_DMZ_SUBNET + default_gateway: 192.168.100.1 + services: + *ST_SRV_WEB_CONFIG + + ############################# + # SOME_TECH INTRANET SUBNET # + ############################# + + - hostname: ST_INTRA-PRV-RT-CR + type: router + ports: + 1: + ip_address: 192.168.150.2 + subnet_mask: *ST_INTRA_CR_SUBNET + 2: + ip_address: 192.168.160.1 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 3: + ip_address: 192.168.170.1 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 4: + ip_address: 192.168.220.1 + subnet_mask: *ST_DATA_SUBNET + + routes: + - address: 192.168.200.0 + subnet_mask: *ST_HO_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.210.0 + subnet_mask: *ST_HR_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.230.0 + subnet_mask: *ST_PROJ_A_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.240.0 + subnet_mask: *ST_PROJ_B_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.250.0 + subnet_mask: *ST_PROJ_C_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + + default_route: + next_hop_ip_address: 192.168.150.1 # ST Public Firewall Internal Port + acl: + 5: + action: PERMIT + + - hostname: ST_INTRA-PRV-RT-DR-1 + type: router + ports: + 1: + ip_address: 192.168.160.2 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 2: + ip_address: 192.168.230.1 + subnet_mask: *ST_PROJ_A_SUBNET + 3: + ip_address: 192.168.240.1 + subnet_mask: *ST_PROJ_B_SUBNET + 4: + ip_address: 192.168.250.1 + subnet_mask: *ST_PROJ_C_SUBNET + default_route: + next_hop_ip_address: 192.168.160.1 # ST Intranet CR Router Port 2 + acl: + 5: + action: PERMIT + + - hostname: ST_INTRA-PRV-RT-DR-2 + type: router + default_gateway: 192.168.170.1 + ports: + 1: + ip_address: 192.168.170.2 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 2: + ip_address: 192.168.200.1 + subnet_mask: *ST_HO_SUBNET + 3: + ip_address: 192.168.210.1 + subnet_mask: *ST_HR_SUBNET + default_route: + next_hop_ip_address: 192.168.170.1 # ST Intranet CR Router Port 3 + acl: + 5: + action: PERMIT + + ################################ + # SOME_TECH HEAD OFFICE SUBNET # + ################################ + + - hostname: ST_HO-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_HO-PRV-PC-1 + type: computer + ip_address: 192.168.200.2 + default_gateway: 192.168.200.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HO-PRV-PC-2 + type: computer + ip_address: 192.168.200.3 + default_gateway: 192.168.200.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HO-PRV-PC-3 + type: computer + ip_address: 192.168.200.4 + default_gateway: 192.168.200.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + #################################### + # SOME_TECH HUMAN RESOURCES SUBNET # + #################################### + + - hostname: ST_HR-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_HR-PRV-PC-1 + type: computer + ip_address: 192.168.210.2 + default_gateway: 192.168.210.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HR-PRV-PC-2 + type: computer + ip_address: 192.168.210.3 + default_gateway: 192.168.210.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HR-PRV-PC-3 + type: computer + ip_address: 192.168.210.4 + default_gateway: 192.168.210.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ######################### + # SOME_TECH DATA SUBNET # + ######################### + + - hostname: ST_DATA-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_DATA-PRV-SRV-STORAGE + type: server + ip_address: *ST_SRV_STORAGE_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_STORAGE_CONFIG + + - hostname: ST_DATA-PRV-SRV-DB + type: server + ip_address: *ST_SRV_DB_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_DB_CONFIG + + ####################### + # SOME_TECH PROJECT A # + ####################### + + - hostname: ST_PROJ-A-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_PROJ-A-PRV-PC-1 + type: computer + ip_address: 192.168.230.2 + default_gateway: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-A-PRV-PC-2 + type: computer + ip_address: 192.168.230.3 + default_gateway: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-A-PRV-PC-3 + type: computer + ip_address: 192.168.230.4 + default_gateway: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT B # + ####################### + + - hostname: ST_PROJ-B-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_PROJ-B-PRV-PC-1 + type: computer + ip_address: 192.168.240.2 + default_gateway: 192.168.240.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-B-PRV-PC-2 + type: computer + ip_address: 192.168.240.3 + default_gateway: 192.168.240.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-B-PRV-PC-3 + type: computer + ip_address: 192.168.240.4 + default_gateway: 192.168.240.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT C # + ####################### + + - hostname: ST_PROJ-C-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_PROJ-C-PRV-PC-1 + type: computer + ip_address: 192.168.250.2 + default_gateway: 192.168.250.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-C-PRV-PC-2 + type: computer + ip_address: 192.168.250.3 + default_gateway: 192.168.250.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-C-PRV-PC-3 + type: computer + ip_address: 192.168.250.4 + default_gateway: 192.168.250.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + + ############################## + # Simulation Network Links # + ############################## + links: + + ############################ + # HOME OFFICE SUBNET LINKS # + ############################ + + # Home Switch (Port 1) --> Home Router (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 1 + + # Home Switch (Port 2) --> PC 1 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: HOME-PUB-PC-1 + endpoint_b_port: 1 + + # Home Switch (Port 3) --> PC 2 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: HOME-PUB-PC-2 + endpoint_b_port: 1 + + # Home Switch (Port 4) --> PC SRV (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: HOME-PUB-SRV + endpoint_b_port: 1 + + ################## + # Internet Links # + ################## + + # Internet Router (Port 1) --> Home Router (Port 2) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 2 + + # Internet Router (Port 2) --> DNS Server (Port 1) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 2 + endpoint_b_hostname: ISP-PUB-SRV-DNS + endpoint_b_port: 1 + + # Internet Router (Port 3) --> Remote Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-FW + endpoint_b_port: 1 + + #################### + # Remote DMZ Links # + #################### + + # Remote Firewall (Internal Port) --> Remote Site Router (Port 1) + - endpoint_a_hostname: REM-PUB-FW + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-RT-DR + endpoint_b_port: 1 + + #################### + # Remote Site Link # + #################### + + # Remote Site Router (Port 2) --> Remote Site Switch (Port 1) + - endpoint_a_hostname: REM-PUB-RT-DR + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-SW-AS + endpoint_b_port: 1 + + # Remote Site Switch (Port 2) --> Remote Site PC 1 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-PC-1 + endpoint_b_port: 1 + + # Remote Site Switch (Port 3) --> Remote Site PC 2 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-PC-2 + endpoint_b_port: 1 + + # Remote Site Switch (Port 4) --> Remote Site Server (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: REM-PUB-SRV + endpoint_b_port: 1 + + ####################### + # SOME_TECH DMZ Links # + ####################### + + # Internet Router (Port 4) --> Some Tech DMZ Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 4 + endpoint_b_hostname: ST_PUB-FW + endpoint_b_port: 1 + + # Some Tech DMZ Firewall (DMZ Port) --> Some Tech Web Server (Port 1) + - endpoint_a_hostname: ST_PUB-FW + endpoint_a_port: 3 + endpoint_b_hostname: ST_DMZ-PUB-SRV-WEB + endpoint_b_port: 1 + + ############################ + # SOME_TECH INTRANET Links # + ############################ + + # Some Tech Intranet CR Router (Port 1) --> Some Tech DMZ Firewall (Internal Port) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 1 + endpoint_b_hostname: ST_PUB-FW + endpoint_b_port: 2 + + # Some Tech Intranet CR Router (Port 2) --> Some Tech Intranet DR Router 1 (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 2 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_b_port: 1 + + # Some Tech Intranet CR Router (Port 3) --> Some Tech Intranet DR Router 2 (Port 2) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 3 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-2 + endpoint_b_port: 1 + + # Some Tech Intranet Private Router CR (Port 4) --> Some Tech Data Private Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 4 + endpoint_b_hostname: ST_DATA-PRV-SW-AS + endpoint_b_port: 1 + + + ############################### + # SOME_TECH HEAD OFFICE Links # + ############################### + + # Some Tech Head Office Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 2) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-2 + endpoint_b_port: 2 + + # Some Tech Head Office Switch (Port 2) --> Some Tech Head Office PC 1 (Port 1) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_HO-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 3) --> Some Tech Head Office PC 2 (Port 1) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_HO-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 4) --> Some Tech Head Office PC 3 (Port 1) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_HO-PRV-PC-3 + endpoint_b_port: 1 + + + ################################### + # SOME_TECH HUMAN RESOURCES Links # + ################################### + + # Some Tech Human Resources Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 3) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-2 + endpoint_b_port: 3 + + # Some Tech Human Resources Switch (Port 2) --> Some Tech Human Resources PC 1 (Port 1) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_HR-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 3) --> Some Tech Human Resources PC 2 (Port 1) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_HR-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 4) --> Some Tech Human Resources PC 3 (Port 1) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_HR-PRV-PC-3 + endpoint_b_port: 1 + + ######################## + # SOME_TECH DATA Links # + ######################## + + # Some Tech Data Switch (Port 2) --> Some Tech Data Private Storage Server (Port 1) + - endpoint_a_hostname: ST_DATA-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_DATA-PRV-SRV-STORAGE + endpoint_b_port: 1 + + # Some Tech Data Switch (Port 3) --> Some Tech Data Private Database Server (Port 1) + + - endpoint_a_hostname: ST_DATA-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_DATA-PRV-SRV-DB + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT A Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 2) --> Some Tech Private Project A Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 2) --> Some Tech Project A PC 1 + - endpoint_a_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-A-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 3) --> Some Tech Project A PC 2 + - endpoint_a_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-A-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 4) --> Some Tech Project A PC 3 + - endpoint_a_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-A-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT B Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 3) --> Some Tech Private Project B Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 2) --> Some Tech Project B PC 1 + - endpoint_a_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-B-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 3) --> Some Tech Project B PC 2 + - endpoint_a_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-B-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 4) --> Some Tech Project B PC 3 + - endpoint_a_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-B-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT C Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 4) --> Some Tech Private Project C Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 2) --> Some Tech Project C PC 1 + - endpoint_a_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-C-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 3) --> Some Tech Project C PC 2 + - endpoint_a_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-C-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 4) --> Some Tech Project C PC 3 + - endpoint_a_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-C-PRV-PC-3 + endpoint_b_port: 1 + +################################## +# Use Case 7 Agent YAML Anchors # +################################## + +############################## +# Green Agent YAML Anchors # +############################## + +# Green Agent Reward Impacts Values # + +LOW_WEIGHT_IMPACT: &LOW_WEIGHT_IMPACT 0.2 +MEDIUM_WEIGHT_IMPACT: &MEDIUM_WEIGHT_IMPACT 0.5 +HIGH_WEIGHT_IMPACT: &HIGH_WEIGHT_IMPACT 0.95 + +LOW_WEIGHT_IMPACT_NEGATIVE: &LOW_WEIGHT_IMPACT_NEG -0.2 +MEDIUM_WEIGHT_IMPACT_NEGATIVE: &MEDIUM_WEIGHT_IMPACT_NEG -0.5 +HIGH_WEIGHT_IMPACT_NEGATIVE: &HIGH_WEIGHT_IMPACT_NEG -0.8 + +# Default Green Agent Action Space Configuration Anchor # + +DEFAULT_GREEN_AGENT_MAX_EXECUTIONS: &DEFAULT_GREEN_AGENT_MAX_EXECUTIONS 1000 # Ensures green agent activity through-out an episode + +################################################# +# Probabilistic Green Agent Config Yaml Anchors # +################################################# + +# Probabilistic Green Agent | 20% node-application-execute | 80% do-nothing # +PROBABILISTIC_CONFIG_20_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_20 + action_probabilities: + 0: 0.8 + 1: 0.2 + +# Probabilistic Green Agent | 40% node-application-execute | 60% do-nothing # +PROBABILISTIC_CONFIG_40_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_40 + action_probabilities: + 0: 0.6 + 1: 0.4 + +# Probabilistic Green Agent | 60% node-application-execute | 40% do-nothing # +PROBABILISTIC_CONFIG_60_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_60 + action_probabilities: + 0: 0.4 + 1: 0.6 + + +# System Green Agent Config UC7 Network Wide Yaml Anchor # + +# Lists the IP_Address of all hosts that contain DNS and NTP Service Clients +UC7_IP_LIST: &UC7_IP_LIST + # ====== Home Office ======== + - 192.168.1.2 # HOME-PUB-PC-1 | ip_id: 2 + - 192.168.1.3 # HOME-PUB-PC-2 | ip_id: 3 + - 192.168.1.4 # HOME-PUB-PC-SRV | ip_id: 4 + # ====== Remote Site ======== + - 192.168.20.2 # REM-PUB-PC-1 | ip_id: 5 + - 192.168.20.3 # REM-PUB-PC-2 | ip_id: 6 + - 192.168.20.4 # REM-PUB-SRV | ip_id: 7 + # ====== ST Public DMZ ======= + - *ST_PUB_SRV_WEB_IP # 192.168.100.2 (ST_DMZ-PUB-SRV-WEB) | ip_id: 8 + # ====== ST Head Office ======= + - 192.168.200.2 # ST_HO-PRV-PC-1 | ip_id: 9 + - 192.168.200.3 # ST_HO-PRV-PC-2 | ip_id: 10 + - 192.168.200.4 # ST_HO-PRV-PC-3 | ip_id: 11 + # ===== ST Human Resources ====== + - 192.168.210.2 # ST_HR-PRV-PC-1 | ip_id: 12 + - 192.168.210.3 # ST_HR-PRV-PC-2 | ip_id: 13 + - 192.168.210.4 # ST_HR-PRV-PC-3 | ip_id: 14 + # ====== ST DATA Servers ======= + - *ST_SRV_STORAGE_IP # 192.168.220.2 (ST_DATA-PRV-SRV-STORAGE) | ip_id: 15 + - *ST_SRV_DB_IP # 192.168.220.3 (ST_DATA-PRV-SRV-DB) | ip_id: 16 + # ====== ST Project A ======= + - 192.168.230.2 # PROJ-A-PRV-PC-1 | ip_id: 17 + - 192.168.230.3 # PROJ-A-PRV-PC-2 | ip_id: 18 + - 192.168.230.4 # PROJ-A-PRV-PC-3 | ip_id: 19 + # ====== ST Project B ======= + - 192.168.240.2 # PROJ-B-PRV-PC-1 | ip_id: 20 + - 192.168.240.3 # PROJ-B-PRV-PC-2 | ip_id: 21 + - 192.168.240.4 # PROJ-B-PRV-PC-3 | ip_id: 22 + # ====== ST Project C ======= + - 192.168.250.2 # PROJ-C-PRV-PC-1 | ip_id: 23 + - 192.168.250.3 # PROJ-C-PRV-PC-2 | ip_id: 24 + - 192.168.250.4 # PROJ-C-PRV-PC-3 | ip_id: 25 + +############################################ +# Use Case 7 Agent Configuration Section # +############################################ + +agents: + ####################################################### + # UC7 Green Agents Path of Life (POL) Configuration # + ####################################################### + + + #################################### + # Home Office Network POL Config # + #################################### + + # Home Office Green Agent Pattern Of Life + # ====================================== + # 1. Three Home workers accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. Three Home workers accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: HOME_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-1"] + target_application: "database-client" + start_step: 4 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-2"] + target_application: "database-client" + start_step: 8 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + - ref: HOME_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + #################################### + # Remote Site Network POL Config # + #################################### + + # Remote Site Green Agent Pattern Of Life + # ====================================== + # 1. Three Remote workers accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. Three Remote workers accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: REMOTE_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-1"] + target_application: "database-client" + start_step: 12 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-2"] + target_application: "database-client" + start_step: 16 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + - ref: REMOTE_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + #################################### + # ST Project A Network POL Config # + #################################### + + # ST Project A Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + + - ref: PROJ_A-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-A-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-1 + + - ref: PROJ_A-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-1 + + - ref: PROJ_A-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-A-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-A-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-A-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-3 + + - ref: PROJ_A-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-A-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-3 + + #################################### + # ST Project B Network POL Config # + #################################### + + # ST Project B Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: PROJ_B-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-B-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-1 + + - ref: PROJ_B-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-B-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-1 + + - ref: PROJ_B-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-B-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-B-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-3 + + - ref: PROJ_B-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-B-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-3 + + #################################### + # ST Project C Network POL Config # + #################################### + + # ST Project C Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: PROJ_C-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-C-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-1 + + - ref: PROJ_C-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-C-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-1 + + - ref: PROJ_C-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-C-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-C-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-C-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-3 + + - ref: PROJ_C-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-3 + + ###################################### + # ST Head Office Network POL Config # + ###################################### + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. The ST CEO accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 2. The ST CTO accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. The ST CFO accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: CEO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HO-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST_HO-PRV-PC-1 + + - ref: CTO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HO-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HO-PRV-PC-2 + + - ref: CFO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HO-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HO-PRV-PC-3 + + ########################################## + # ST Human Resources Network POL Config # + ########################################## + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. A senior HR staff accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 2. Two junior HR staff accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: SENIOR_HR + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HR-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HR-PRV-PC-1 + + - ref: JUNIOR_HR-1 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HR-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HR-PRV-PC-2 + + - ref: JUNIOR_HR-2 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HR-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HR-PRV-PC-3 + + ########################## + # UC7 Red Agent Config # + ########################## + + - ref: attacker + team: RED + type: tap-003 + observation_space: {} + action_space: {} + agent_settings: + start_step: 1 + frequency: 3 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_starting_node: "ST_PROJ-A-PRV-PC-1" + starting_nodes: + # starting_nodes: ["ST_PROJ-A-PRV-PC-1", "ST_PROJ-B-PRV-PC-2", "ST_PROJ-C-PRV-PC-3"] + kill_chain: + PLANNING: + probability: 1 + starting_network_knowledge: + credentials: + ST_PROJ-A-PRV-PC-1: + username: admin + password: admin + ST_PROJ-B-PRV-PC-2: + username: admin + password: admin + ST_PROJ-C-PRV-PC-3: + username: admin + password: admin + ST_INTRA-PRV-RT-DR-1: + ip_address: 192.168.230.1 + username: admin + password: admin + ST_INTRA-PRV-RT-CR: + ip_address: 192.168.160.1 + username: admin + password: admin + REM-PUB-RT-DR: + ip_address: 192.168.10.2 + username: admin + password: admin + ACCESS: + probability: 1 + MANIPULATION: + probability: 1 + account_changes: + - host: ST_INTRA-PRV-RT-DR-1 + ip_address: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 + action: change_password + username: admin + new_password: "red_pass" + - host: ST_INTRA-PRV-RT-CR + ip_address: 192.168.160.1 # ST_INTRA-PRV-RT-CR + action: change_password + username: "admin" + new_password: "red_pass" + - host: REM-PUB-RT-DR + ip_address: 192.168.10.2 # REM-PUB-RT-DR + action: change_password + username: "admin" + new_password: "red_pass" + EXPLOIT: + probability: 1 + malicious_acls: + - target_router: ST_INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_port: POSTGRES_SERVER + protocol_name: TCP + - target_router: ST_INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: HTTP + dst_port: HTTP + protocol_name: TCP + - target_router: REM-PUB-RT-DR + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: DNS + dst_port: DNS + protocol_name: TCP + +########################### +# UC7 Blue Agent Config # +########################### + + - ref: defender + team: BLUE + type: proxy-agent + observation_space: + type: custom + options: + components: + - type: nodes + label: NODES + options: + hosts: + # TAP001 Potential Starting Note | ST_PROJ-A-PRV-PC-1 + - hostname: ST_PROJ-A-PRV-PC-1 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST_PROJ-B-PRV-PC-2 + - hostname: ST_PROJ-B-PRV-PC-2 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST_PROJ-C-PRV-PC-3 + - hostname: ST_PROJ-C-PRV-PC-3 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # ST DATA Server Database + - hostname: ST_DATA-PRV-SRV-DB + folders: + - folder_name: database + files: + - file_name: database.db + routers: + - hostname: ST_INTRA-PRV-RT-CR # TAP003 Malicious ACL Target | ROUTER0 + - hostname: ST_INTRA-PRV-RT-DR-1 # TAP003 Malicious ACL Target | ROUTER1 + - hostname: REM-PUB-RT-DR # TAP003 Malicious ACL Target | ROUTER2 + num_ports: 5 + num_services: 2 + num_applications: 2 + num_folders: 2 + num_files: 1 + num_nics: 1 + ip_list: + # ip_list is indexed at 2: + # 0 reserved for padding to align with observations + # 1 reserved for ALL ips + *UC7_IP_LIST + wildcard_list: + - 0.0.0.1 # 0 + - 0.0.0.255 # 1 + - 0.0.255.255 # 2 + port_list: + # 0 is a padding placeholder + # 1 means ALL ports + - FTP # 2 + - DNS # 3 + - HTTP # 4 + - NTP # 5 + - POSTGRES_SERVER # 6 + - SSH # 7 + + protocol_list: + # 0 is padding placeholder + # 1 means ALL protocols + - ICMP # 2 + - TCP # 3 + - UDP # 4 + num_rules: 5 + include_num_access: true + include_nmne: true + monitored_traffic: + tcp: + - HTTP + - POSTGRES_SERVER + icmp: + - NONE + - type: links + label: LINKS + options: + link_references: + # HOME OFFICE SUBNET LINKS + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-RT-DR:eth-1 # 1 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-1:eth-1 # 2 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-2:eth-1 # 3 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-SRV:eth-1 # 4 + # Internet LINKS + - ISP-PUB-RT-BR:eth-1<->HOME-PUB-RT-DR:eth-2 # 5 + - ISP-PUB-RT-BR:eth-2<->ISP-PUB-SRV-DNS:eth-1 # 6 + - ISP-PUB-RT-BR:eth-3<->REM-PUB-FW:eth-1 # 7 + # Remote DMZ Links + - REM-PUB-FW:eth-2<->REM-PUB-RT-DR:eth-1 # 8 + # Remote Site Links + - REM-PUB-RT-DR:eth-2<->REM-PUB-SW-AS:eth-1 # 9 + - REM-PUB-SW-AS:eth-2<->REM-PUB-PC-1:eth-1 # 10 + - REM-PUB-SW-AS:eth-3<->REM-PUB-PC-2:eth-1 # 11 + - REM-PUB-SW-AS:eth-4<->REM-PUB-SRV:eth-1 # 12 + # SOME_TECH DMZ + - ISP-PUB-RT-BR:eth-4<->ST_PUB-FW:eth-1 # 13 + - ST_PUB-FW:eth-3<->ST_DMZ-PUB-SRV-WEB:eth-1 # 14 + # SOME_TECH Intranet + - ST_INTRA-PRV-RT-CR:eth-1<->ST_PUB-FW:eth-2 # 15 + - ST_INTRA-PRV-RT-CR:eth-2<->ST_INTRA-PRV-RT-DR-1:eth-1 # 16 + - ST_INTRA-PRV-RT-CR:eth-3<->ST_INTRA-PRV-RT-DR-2:eth-1 # 17 + - ST_INTRA-PRV-RT-CR:eth-4<->ST_DATA-PRV-SW-AS:eth-1 # 18 + # SOME_TECH Head Office + - ST_HO-PRV-SW-AS:eth-1<->ST_INTRA-PRV-RT-DR-2:eth-2 # 19 + - ST_HO-PRV-SW-AS:eth-2<->ST_HO-PRV-PC-1:eth-1 # 20 + - ST_HO-PRV-SW-AS:eth-3<->ST_HO-PRV-PC-2:eth-1 # 21 + - ST_HO-PRV-SW-AS:eth-4<->ST_HO-PRV-PC-3:eth-1 # 22 + # SOME_TECH Human Resources + - ST_HR-PRV-SW-AS:eth-1<->ST_INTRA-PRV-RT-DR-2:eth-3 # 23 + - ST_HR-PRV-SW-AS:eth-2<->ST_HR-PRV-PC-1:eth-1 # 24 + - ST_HR-PRV-SW-AS:eth-3<->ST_HR-PRV-PC-2:eth-1 # 25 + - ST_HR-PRV-SW-AS:eth-4<->ST_HR-PRV-PC-3:eth-1 # 26 + # SOME_TECH Data Links + - ST_DATA-PRV-SW-AS:eth-2<->ST_DATA-PRV-SRV-STORAGE:eth-1 # 27 + - ST_DATA-PRV-SW-AS:eth-3<->ST_DATA-PRV-SRV-DB:eth-1 # 28 + # SOME_TECH Project A Links + - ST_INTRA-PRV-RT-DR-1:eth-2<->ST_PROJ-A-PRV-SW-AS:eth-1 # 29 + - ST_PROJ-A-PRV-SW-AS:eth2<->ST_PROJ-A-PRV-PC-1:eth-1 # 31 + - ST_PROJ-A-PRV-SW-AS:eth3<->ST_PROJ-A-PRV-PC-2:eth-1 # 32 + - ST_PROJ-A-PRV-SW-AS:eth4<->ST_PROJ-A-PRV-PC-3:eth-1 # 33 + # SOME_TECH Project B Links + - ST_INTRA-PRV-RT-DR-1:eth-3<->ST_PROJ-B-PRV-SW-AS:eth-1 # 34 + - ST_PROJ-B-PRV-SW-AS:eth2<->ST_PROJ-B-PRV-PC-1:eth-1 # 35 + - ST_PROJ-B-PRV-SW-AS:eth3<->ST_PROJ-B-PRV-PC-2:eth-1 # 36 + - ST_PROJ-B-PRV-SW-AS:eth4<->ST_PROJ-B-PRV-PC-3:eth-1 # 37 + # SOME_TECH Project C Links + - ST_INTRA-PRV-RT-DR-1:eth-4<->ST_PROJ-C-PRV-SW-AS:eth-1 # 38 + - ST_PROJ-A-PRV-SW-AS:eth2<->ST_PROJ-C-PRV-PC-1:eth-1 # 39 + - ST_PROJ-A-PRV-SW-AS:eth3<->ST_PROJ-C-PRV-PC-2:eth-1 # 40 + - ST_PROJ-A-PRV-SW-AS:eth4<->ST_PROJ-C-PRV-PC-3:eth-1 # 41 + action_space: + action_map: + 0: + action: do-nothing + options: {} + + # |======================================| + # | ST_PROJ-A-PRV-PC-1 | + # |======================================| + + # ST_PROJ-A-PRV-PC-1 | node-os-scan + 1: + action: node-os-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | node-shutdown + 2: + action: node-shutdown + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | node-startup + 3: + action: node-startup + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | node-reset + 4: + action: node-reset + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | host-nic-disable + 5: + action: host-nic-disable + options: + node_name: ST_PROJ-A-PRV-PC-1 + nic_num: 0 + # ST_PROJ-A-PRV-PC-1 | host-nic-enable + 6: + action: host-nic-enable + options: + node_name: ST_PROJ-A-PRV-PC-1 + nic_num: 0 + # ST_PROJ-A-PRV-PC-1 | node-application-close | database-client + 7: + action: node-application-close + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + # ST_PROJ-A-PRV-PC-1 | node-application-scan | database-client + 8: + action: node-application-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + # ST_PROJ-A-PRV-PC-1 | node-application-fix | database-client + 9: + action: node-application-fix + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + # ST_PROJ-A-PRV-PC-1 | node-application-remove | database-client + 10: + action: node-application-remove + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + + # ST_PROJ-A-PRV-PC-1 | node-file-scan | downloads/malware_dropper.ps1 + 11: + action: node-file-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST_PROJ-A-PRV-PC-1 | node-file-scan | exfiltration_folder/database.db + 12: + action: node-file-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + file_name: database.db + + # ST_PROJ-A-PRV-PC-1 | node-folder-scan | downloads/ + 13: + action: node-folder-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: downloads + + # ST_PROJ-A-PRV-PC-1 | node-folder-scan | exfiltration_folder/ + 14: + action: node-folder-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + + # |======================================| + # | ST_PROJ-B-PRV-PC-2 | + # |======================================| + + # ST_PROJ-B-PRV-PC-2 | node-os-scan + 15: + action: node-os-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | node-shutdown + 16: + action: node-shutdown + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | node-startup + 17: + action: node-startup + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | node-reset + 18: + action: node-reset + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | host-nic-disable + 19: + action: host-nic-disable + options: + node_name: ST_PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST_PROJ-B-PRV-PC-2 | host-nic-enable + 20: + action: host-nic-enable + options: + node_name: ST_PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST_PROJ-B-PRV-PC-2 | node-application-close | database-client + 21: + action: node-application-close + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-application-scan | database-client + 22: + action: node-application-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-application-fix | database-client + 23: + action: node-application-fix + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-application-remove | database-client + 24: + action: node-application-remove + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-file-scan | downloads/malware_dropper.ps1 + 25: + action: node-file-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST_PROJ-B-PRV-PC-2 | node-file-scan | exfiltration_folder/database.db + 26: + action: node-file-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + file_name: database.db + + # ST_PROJ-B-PRV-PC-2 | node-folder-scan | downloads/ + 27: + action: node-folder-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: downloads + + # ST_PROJ-B-PRV-PC-2 | node-folder-scan | exfiltration_folder/ + 28: + action: node-folder-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + + # |======================================| + # | ST_PROJ-C-PRV-PC-3 | + # |======================================| + + # ST_PROJ-C-PRV-PC-3 | node-os-scan + 29: + action: node-os-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | node-shutdown + 30: + action: node-shutdown + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | node-startup + 31: + action: node-startup + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | node-reset + 32: + action: node-reset + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | host-nic-disable + 33: + action: host-nic-disable + options: + node_name: ST_PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST_PROJ-C-PRV-PC-3 | host-nic-enable + 34: + action: host-nic-enable + options: + node_name: ST_PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST_PROJ-C-PRV-PC-3 | node-application-close | database-client + 35: + action: node-application-close + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-application-scan | database-client + 36: + action: node-application-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-application-fix | database-client + 37: + action: node-application-fix + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-application-remove | database-client + 38: + action: node-application-remove + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-file-scan | downloads/malware_dropper.ps1 + 39: + action: node-file-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST_PROJ-C-PRV-PC-3 | node-file-scan | exfiltration_folder/database.db + 40: + action: node-file-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + file_name: database.db + + # ST_PROJ-C-PRV-PC-3 | node-folder-scan | downloads/ + 41: + action: node-folder-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: downloads + + # ST_PROJ-C-PRV-PC-3 | node-folder-scan | exfiltration_folder/ + 42: + action: node-folder-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + + # |======================================| + # | ST_INTRA-PRV-RT-CR | + # |======================================| + + # ST_INTRA-PRV-RT-CR | router-acl-addrule | P2: ST_PROJ-A-PRV-PC-1 !==> ST_DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 43: + action: router-acl-addrule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST_PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST_DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST_INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 1 + 44: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 1 + + # ST_INTRA-PRV-RT-CR | router-acl-addrule | P3: ST_PROJ-B-PRV-PC-2 !==> ST_DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 45: + action: router-acl-addrule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 2 + permission: DENY + src_ip: 192.168.240.3 # (ST_PROJ-B-PRV-PC-2) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST_DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + # ST_INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 2 + 46: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 2 + + # ST_INTRA-PRV-RT-CR | router-acl-addrule | P4: ST_PROJ-C-PRV-PC-3 !==> ST_DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 47: + action: router-acl-addrule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 3 + permission: DENY + src_ip: 192.168.250.4 # (ST_PROJ-C-PRV-PC-3) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST_DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST_INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 3 + 48: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 3 + + # |======================================| + # | ST_DATA-PRV-SRV-DB | + # |======================================| + + # ST_DATA-PRV-SRV-DB | node-file-scan | Scans the database.db file (health status) + 49: + action: node-file-scan + options: + node_name: ST_DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # ST_DATA-PRV-SRV-DB | node-account-change-password | Changes the password of a user account + 50: + action: node-account-change-password + options: + node_name: ST_DATA-PRV-SRV-DB + username: admin # default account + current_password: admin # default password + new_password: thr33_alert_wolv3z # A more 'secure' password + + # |======================================| + # | ST_INTRA-PRV-RT-DR-1 | + # |======================================| + + # ST_INTRA-PRV-RT-DR-1 | router-acl-addrule | P1: ST_INTRA-PRV-RT-DR-1 !==> ANY (TCP:SSH) + 51: + action: router-acl-addrule + options: + target_router: ST_INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST_PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: SSH + dst_ip: ALL + dst_wildcard: 0.0.255.255 + dst_port: SSH + protocol_name: TCP + + # ST_INTRA-PRV-RT-DR-1 | node-account-change-password + 52: + action: node-account-change-password + options: + node_name: ST_INTRA-PRV-RT-DR-1 + username: admin + current_password: admin + new_password: secure_password + + # ST_INTRA-PRV-RT-DR-1 | router-acl-remove-rule | Removes the given ACL at position 1 + 53: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-DR-1 + position: 1 + + # |======================================| + # | REM-PUB-RT-DR | + # |======================================| + + # REM-PUB-RT-DR | node-account-change-password + 54: + action: node-account-change-password + options: + node_name: REM-PUB-RT-DR + username: admin + current_password: admin + new_password: secure_password + + # REM-PUB-RT-DR | router-acl-remove-rule | Removes the given ACL at position 1 + 55: + action: router-acl-remove-rule + options: + target_router: REM-PUB-RT-DR + position: 1 + + reward_function: + reward_components: + - type: database-file-integrity + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST_DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # Home Site Green Agents (32 Green Agents each contributing 0.03125 of blue reward) + + # Blue Shared Reward | HOME_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-DB + + # Blue Shared Reward | HOME_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-WEB + + # Blue Shared Reward | HOME_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-DB + + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-WEB + + # Remote Site Green Agents + + # Blue Shared Reward | REMOTE_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-DB + + # Blue Shared Reward | REMOTE_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-WEB + + # Blue Shared Reward | REMOTE_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-DB + + # Blue Shared Reward | REMOTE_WORKER-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-WEB + + # ST Project A Green Agents + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-WEB + + # ST Project B Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Project C Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Head Office Green Agents (CEO/CFO/CTO) + + # Blue Shared Reward | CEO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CEO + + # Blue Shared Reward | CFO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CFO + + # Blue Shared Reward | CTO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CTO + + # ST Human Resources Green Agents + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: SENIOR_HR + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-1 + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-2 + + agent_settings: + flatten_obs: True diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC1.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC1.yaml new file mode 100644 index 00000000..721cec6e --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC1.yaml @@ -0,0 +1,40 @@ +red: &red + - ref: attacker + team: RED + type: tap-001 + agent_settings: + start_step: 1 + frequency: 5 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_target_ip: 192.168.220.3 + default_starting_node: "ST_PROJ-C-PRV-PC-1" + starting_nodes: + kill_chain: + ACTIVATE: + probability: 1 + PROPAGATE: + probability: 1 + scan_attempts: 20 + repeat_scan: false + network_addresses: + - 192.168.230.0/29 # ST Project A + - 192.168.10.0/26 # Remote Site + - 192.168.20.0/30 # Remote DMZ + - 192.168.220.0/29 # ST Data (Contains Target) + COMMAND_AND_CONTROL: + probability: 1 + keep_alive_frequency: 5 + masquerade_port: HTTP + masquerade_protocol: TCP + c2_server_name: ISP-PUB-SRV-DNS + c2_server_ip: 8.8.8.8 + PAYLOAD: + probability: 1 + exfiltrate: true + corrupt: true + exfiltration_folder_name: + target_username: admin + target_password: admin + continue_on_failed_exfil: True diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC2.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC2.yaml new file mode 100644 index 00000000..382a2d40 --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC2.yaml @@ -0,0 +1,40 @@ +red: &red + - ref: attacker + team: RED + type: tap-001 + agent_settings: + start_step: 1 + frequency: 5 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_target_ip: 192.168.220.3 + default_starting_node: "ST_PROJ-B-PRV-PC-2" + starting_nodes: + kill_chain: + ACTIVATE: + probability: 1 + PROPAGATE: + probability: 1 + scan_attempts: 20 + repeat_scan: false + network_addresses: + - 192.168.240.0/29 # ST Project B + - 192.168.10.0/26 # Remote Site + - 192.168.20.0/30 # Remote DMZ + - 192.168.220.0/29 # ST Data (Contains Target) + COMMAND_AND_CONTROL: + probability: 1 + keep_alive_frequency: 5 + masquerade_port: HTTP + masquerade_protocol: TCP + c2_server_name: ISP-PUB-SRV-DNS + c2_server_ip: 8.8.8.8 + PAYLOAD: + probability: 1 + exfiltrate: true + corrupt: true + exfiltration_folder_name: + target_username: admin + target_password: admin + continue_on_failed_exfil: True diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC3.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC3.yaml new file mode 100644 index 00000000..ad418b32 --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP001_PC3.yaml @@ -0,0 +1,40 @@ +red: &red + - ref: attacker + team: RED + type: tap-001 + agent_settings: + start_step: 1 + frequency: 5 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_target_ip: 192.168.220.3 + default_starting_node: "ST_PROJ-C-PRV-PC-3" + starting_nodes: + kill_chain: + ACTIVATE: + probability: 1 + PROPAGATE: + probability: 1 + scan_attempts: 20 + repeat_scan: false + network_addresses: + - 192.168.250.0/29 # ST Project C + - 192.168.10.0/26 # Remote Site + - 192.168.20.0/30 # Remote DMZ + - 192.168.220.0/29 # ST Data (Contains Target) + COMMAND_AND_CONTROL: + probability: 1 + keep_alive_frequency: 5 + masquerade_port: HTTP + masquerade_protocol: TCP + c2_server_name: ISP-PUB-SRV-DNS + c2_server_ip: 8.8.8.8 + PAYLOAD: + probability: 1 + exfiltrate: true + corrupt: true + exfiltration_folder_name: + target_username: admin + target_password: admin + continue_on_failed_exfil: True diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP003.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP003.yaml new file mode 100644 index 00000000..aae7e9b4 --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/TAP003.yaml @@ -0,0 +1,94 @@ +red: &red + - ref: attacker + team: RED + type: tap-003 + observation_space: {} + action_space: {} + agent_settings: + start_step: 1 + frequency: 3 + variance: 0 + repeat_kill_chain: false + repeat_kill_chain_stages: true + default_starting_node: "ST_PROJ-A-PRV-PC-1" + starting_nodes: + # starting_nodes: ["ST_PROJ-A-PRV-PC-1", "ST_PROJ-B-PRV-PC-2", "ST_PROJ-C-PRV-PC-3"] + kill_chain: + PLANNING: + probability: 1 + starting_network_knowledge: + credentials: + ST_PROJ-A-PRV-PC-1: + username: admin + password: admin + ST_PROJ-B-PRV-PC-2: + username: admin + password: admin + ST_PROJ-C-PRV-PC-3: + username: admin + password: admin + ST_INTRA-PRV-RT-DR-1: + ip_address: 192.168.230.1 + username: admin + password: admin + ST_INTRA-PRV-RT-CR: + ip_address: 192.168.160.1 + username: admin + password: admin + REM-PUB-RT-DR: + ip_address: 192.168.10.2 + username: admin + password: admin + ACCESS: + probability: 1 + MANIPULATION: + probability: 1 + account_changes: + - host: ST_INTRA-PRV-RT-DR-1 + ip_address: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 + action: change_password + username: admin + new_password: "red_pass" + - host: ST_INTRA-PRV-RT-CR + ip_address: 192.168.160.1 # ST_INTRA-PRV-RT-CR + action: change_password + username: "admin" + new_password: "red_pass" + - host: REM-PUB-RT-DR + ip_address: 192.168.10.2 # REM-PUB-RT-DR + action: change_password + username: "admin" + new_password: "red_pass" + EXPLOIT: + probability: 1 + malicious_acls: + - target_router: ST_INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_port: POSTGRES_SERVER + protocol_name: TCP + - target_router: ST_INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: HTTP + dst_port: HTTP + protocol_name: TCP + - target_router: REM-PUB-RT-DR + position: 1 + permission: DENY + src_ip: ALL + src_wildcard: 0.0.255.255 + dst_ip: ALL + dst_wildcard: 0.0.255.255 + src_port: DNS + dst_port: DNS + protocol_name: TCP diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/schedule.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/schedule.yaml new file mode 100644 index 00000000..e11c2fbf --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/schedule.yaml @@ -0,0 +1,42 @@ +base_scenario: uc7_config_no_red.yaml +schedule: + 0: + - TAP001_PC1.yaml + 1: + - TAP001_PC2.yaml + 2: + - TAP001_PC3.yaml + 3: + - TAP001_PC1.yaml + 4: + - TAP001_PC2.yaml + 5: + - TAP003.yaml + 6: + - TAP003.yaml + 7: + - TAP003.yaml + 8: + - TAP003.yaml + 9: + - TAP003.yaml + 10: + - TAP001_PC1.yaml + 11: + - TAP003.yaml + 12: + - TAP001_PC1.yaml + 13: + - TAP003.yaml + 14: + - TAP001_PC2.yaml + 15: + - TAP003.yaml + 16: + - TAP001_PC3.yaml + 17: + - TAP003.yaml + 18: + - TAP001_PC1.yaml + 19: + - TAP003.yaml diff --git a/src/primaite/config/_package_data/uc7_multiple_attack_variants/uc7_config_no_red.yaml b/src/primaite/config/_package_data/uc7_multiple_attack_variants/uc7_config_no_red.yaml new file mode 100644 index 00000000..4a49175b --- /dev/null +++ b/src/primaite/config/_package_data/uc7_multiple_attack_variants/uc7_config_no_red.yaml @@ -0,0 +1,2635 @@ +########################################################## +# USE CASE 7 CONFIGURATION YAML FILE # +########################################################## + + +########################################## +# PrimAITE Game and Logging Settings # +########################################## + + +# PrimAITE I/O Settings # + +io_settings: + save_agent_actions: True + save_step_metadata: True + save_pcap_logs: false + save_sys_logs: True + # save_sys_logs: true + write_sys_log_to_terminal: false + + +# PrimAITE Game Settings # + +game: + max_episode_length: 128 + ports: + - FTP + - DNS + - HTTP + - NTP + - POSTGRES_SERVER + - SSH + protocols: + - ICMP + - TCP + - UDP + thresholds: + nmne: + high: 10 + medium: 5 + low: 0 + +############################################ +# PrimAITE Use Case 7 Simulation # +############################################ + +########################################## +# Configuration Variables (Yaml Anchors) # +########################################## + +# External Network Address List # + +DNS_SUBNET: &DNS_SUBNET 255.255.255.240 # | 8.8.8.0 / 28 +HOME_INTERNET_SUBNET: &HOME_INTERNET_SUBNET 255.255.255.252 # | 10.1.0.0 / 30 +REMOTE_INTERNET_SUBNET: &REMOTE_INTERNET_SUBNET 255.255.255.252 # | 10.1.10.0 / 30 +SOME_TECH_INTERNET_SUBNET: &ST_INTERNET_SUBNET 255.255.255.252 # | 10.1.100.0 / 30 +HOME_OFFICE_SUBNET: &HOME_SUBNET 255.255.255.0 # | 192.168.1.0 / 26 +REMOTE_SUBNET_DMZ: &REMOTE_SUBNET_DMZ 255.255.255.252 # | 192.168.10.0 / 30 +REMOTE_SUBNET: &REMOTE_SUBNET 255.255.255.240 # | 192.168.20.0 / 28 + +# SOME_TECH (ST) Network Address List # + +SOME_TECH_DMZ_SUBNET: &ST_DMZ_SUBNET 255.255.255.252 # | 192.168.100.0 / 30 +SOME_TECH_INTRANET_RT_CR_SUBNET: &ST_INTRA_CR_SUBNET 255.255.255.240 # | 192.168.150.0 / 28 +SOME_TECH_INTRANET_RT_DR_ONE_SUBNET: &ST_INTRA_DR_ONE_SUBNET 255.255.255.252 # | 192.168.160.0 / 30 +SOME_TECH_INTRANET_RT_DR_TWO_SUBNET: &ST_INTRA_DR_TWO_SUBNET 255.255.255.252 # | 192.168.170.0 / 30 +SOME_TECH_HEAD_OFFICE_SUBNET: &ST_HO_SUBNET 255.255.255.248 # | 192.168.200.0 / 29 +SOME_TECH_HUMAN_RESOURCES_SUBNET: &ST_HR_SUBNET 255.255.255.248 # | 192.168.210.0 / 29 +SOME_TECH_DATA_SUBNET: &ST_DATA_SUBNET 255.255.255.248 # | 192.168.220.0 / 29 +SOME_TECH_PROJECT_A_SUBNET: &ST_PROJ_A_SUBNET 255.255.255.248 # | 192.168.230.0 / 29 +SOME_TECH_PROJECT_B_SUBNET: &ST_PROJ_B_SUBNET 255.255.255.248 # | 192.168.240.0 / 29 +SOME_TECH_PROJECT_C_SUBNET: &ST_PROJ_C_SUBNET 255.255.255.248 # | 192.168.250.0 / 29 + +# Host & Server Configurations # + +# ST Public Web Server | web-server | ST_DMZ-PUB-SRV-WEB +SOME_TECH_PUBLIC_SERVER_WEB_IP_ADDRESS: &ST_PUB_SRV_WEB_IP 192.168.100.2 +SOME_TECH_PUBLIC_SERVER_WEB_CONFIG: &ST_SRV_WEB_CONFIG # + - type: web-server + +# ISP Public DNS | dns-server | ISP-PUB-SRV-DNS +PUBLIC_DNS_IP_ADDRESS: &PUBLIC_DNS_IP 8.8.8.8 +PUBLIC_DNS_CONFIG: &PUBLIC_DNS_CONFIG # + - type: dns-server + options: + domain_mapping: + some_tech.com: *ST_PUB_SRV_WEB_IP + +# ST Private Storage Server | ftp-server | ST_DATA-PRV-SRV-STORAGE +SOME_TECH_PRIVATE_SERVER_STORAGE_IP: &ST_SRV_STORAGE_IP 192.168.220.2 +SOME_TECH_PRIVATE_SERVER_STORAGE_CONFIG: &ST_SRV_STORAGE_CONFIG + - type: ftp-server + +# ST Private Database Server | database-client & ftp-client | ST_DATA-PRV-SRV-DB +SOME_TECH_PRIVATE_SERVER_DATABASE_IP: &ST_SRV_DB_IP 192.168.220.3 +SOME_TECH_PRIVATE_SERVER_DATABASE_CONFIG: &ST_SRV_DB_CONFIG + - type: database-service + options: + backup_server_ip: *ST_SRV_STORAGE_IP + - type: ftp-client + +# Default PC Configuration | Database Client & Web Server +PERSONAL_COMPUTER_DEFAULT_CONFIG: &PC_DEFAULT_CONFIG + - type: database-client + options: + db_server_ip: *ST_SRV_DB_IP + - type: web-browser + options: + target_url: http://some_tech.com + + +############################## +# Simulation Configuration # +############################## + +simulation: + defaults: + folder_scan_duration: 0 + folder_restore_duration: 3 + service_fix_duration: 2 + service_restart_duration: 2 + software_install_duration: 0 + node_start_up_duration: 3 + node_shut_down_duration: 3 + node_scan_duration: 8 + network: + nmne_config: + capture_nmne: true + nmne_capture_keywords: + - DELETE + - ENCRYPT + nodes: + ###################### + # HOME OFFICE SUBNET # + ###################### + - hostname: HOME-PUB-RT-DR + type: router + default_gateway: 10.1.0.1 + ports: + 1: + ip_address: 192.168.1.1 + subnet_mask: *HOME_SUBNET + 2: + ip_address: 10.1.0.2 + subnet_mask: *HOME_INTERNET_SUBNET + default_route: + next_hop_ip_address: 10.1.0.1 + acl: + 5: + action: PERMIT + + - hostname: HOME-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: HOME-PUB-PC-1 + type: computer + ip_address: 192.168.1.2 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-PC-2 + type: computer + ip_address: 192.168.1.3 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: HOME-PUB-SRV + type: server + ip_address: 192.168.1.4 + default_gateway: 192.168.1.1 + dns_server: *PUBLIC_DNS_IP + + ###################### + # INTERNET SUBNET # + ###################### + - hostname: ISP-PUB-RT-BR + type: router + ports: + 1: + ip_address: 10.1.0.1 + subnet_mask: *HOME_INTERNET_SUBNET + 2: + ip_address: 8.8.8.1 + subnet_mask: *DNS_SUBNET + 3: + ip_address: 10.1.10.1 + subnet_mask: *REMOTE_INTERNET_SUBNET + 4: + ip_address: 10.1.100.1 + subnet_mask: *ST_INTERNET_SUBNET + routes: + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.0.2 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 8.8.8.8 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.10.2 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.10.2 + + default_route: + next_hop_ip_address: 10.1.100.2 # SOME_TECH Firewall + + acl: + 5: + action: PERMIT + + ################ + # DNS SUBNET # + ################ + - hostname: ISP-PUB-SRV-DNS + type: server + ip_address: 8.8.8.8 + subnet_mask: *DNS_SUBNET + default_gateway: 8.8.8.1 + services: + *PUBLIC_DNS_CONFIG + applications: + - type: c2-server # Represents the external internet. + options: + listen_on_ports: + - 80 + - 53 + - 21 + ######################## + # REMOTE SITE SUBNET # + ######################## + - hostname: REM-PUB-FW + type: firewall + ports: + external_port: # Public Internet facing + ip_address: 10.1.10.2 + subnet_mask: *REMOTE_INTERNET_SUBNET + internal_port: # Remote Site (DMZ Subnet) facing + ip_address: 192.168.10.1 + subnet_mask: *REMOTE_SUBNET_DMZ + routes: + - address: 192.168.20.0 # Remote Site Network + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 192.168.10.2 + default_route: + next_hop_ip_address: 10.1.10.1 # Forward to internet router port 3 + acl: + internal_inbound_acl: + 1: + action: PERMIT + internal_outbound_acl: + 1: + action: PERMIT + dmz_inbound_acl: + 1: + action: PERMIT + dmz_outbound_acl: + 1: + action: PERMIT + external_inbound_acl: + 1: + action: PERMIT + external_outbound_acl: + 1: + action: PERMIT + + - hostname: REM-PUB-RT-DR + type: router + default_gateway: 192.168.10.1 + ports: + 1: + ip_address: 192.168.10.2 + subnet_mask: *REMOTE_SUBNET_DMZ + 2: + ip_address: 192.168.20.1 + subnet_mask: *REMOTE_SUBNET + default_route: + next_hop_ip_address: 192.168.10.1 + acl: + 5: + action: PERMIT + + - hostname: REM-PUB-SW-AS + type: switch + num_ports: 5 + + - hostname: REM-PUB-PC-1 + type: computer + ip_address: 192.168.20.2 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-PC-2 + type: computer + ip_address: 192.168.20.3 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: REM-PUB-SRV + type: server + ip_address: 192.168.20.4 + default_gateway: 192.168.20.1 + dns_server: *PUBLIC_DNS_IP + + + ######################## + # SOME_TECH DMZ SUBNET # + ######################## + + - hostname: ST_PUB-FW + type: firewall + ports: + external_port: # Public Internet Facing Port + ip_address: 10.1.100.2 + subnet_mask: *ST_INTERNET_SUBNET + internal_port: # SOME_TECH Intranet Port + ip_address: 192.168.150.1 + subnet_mask: *ST_INTRA_CR_SUBNET + dmz_port: # SOME_TECH Port Facing Port + ip_address: 192.168.100.1 + subnet_mask: *ST_DMZ_SUBNET + acl: + internal_inbound_acl: + 5: + action: PERMIT + internal_outbound_acl: + 5: + action: PERMIT + dmz_inbound_acl: + 5: + action: PERMIT + dmz_outbound_acl: + 5: + action: PERMIT + external_inbound_acl: + 5: + action: PERMIT + external_outbound_acl: + 5: + action: PERMIT + routes: + - address: 10.1.100.0 + subnet_mask: *ST_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.10.0 + subnet_mask: *REMOTE_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 10.1.0.0 + subnet_mask: *HOME_INTERNET_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.1.0 + subnet_mask: *HOME_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.10.0 + subnet_mask: *REMOTE_SUBNET_DMZ + next_hop_ip_address: 10.1.100.1 + + - address: 192.168.20.0 + subnet_mask: *REMOTE_SUBNET + next_hop_ip_address: 10.1.100.1 + + - address: 8.8.8.0 + subnet_mask: *DNS_SUBNET + next_hop_ip_address: 10.1.100.1 + default_route: + next_hop_ip_address: 192.168.150.2 + + - hostname: ST_DMZ-PUB-SRV-WEB + type: server + ip_address: *ST_PUB_SRV_WEB_IP + subnet_mask: *ST_DMZ_SUBNET + default_gateway: 192.168.100.1 + services: + *ST_SRV_WEB_CONFIG + + ############################# + # SOME_TECH INTRANET SUBNET # + ############################# + + - hostname: ST_INTRA-PRV-RT-CR + type: router + ports: + 1: + ip_address: 192.168.150.2 + subnet_mask: *ST_INTRA_CR_SUBNET + 2: + ip_address: 192.168.160.1 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 3: + ip_address: 192.168.170.1 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 4: + ip_address: 192.168.220.1 + subnet_mask: *ST_DATA_SUBNET + + routes: + - address: 192.168.200.0 + subnet_mask: *ST_HO_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.210.0 + subnet_mask: *ST_HR_SUBNET + next_hop_ip_address: 192.168.170.2 # ST Intra Router Two + - address: 192.168.230.0 + subnet_mask: *ST_PROJ_A_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.240.0 + subnet_mask: *ST_PROJ_B_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + - address: 192.168.250.0 + subnet_mask: *ST_PROJ_C_SUBNET + next_hop_ip_address: 192.168.160.2 # ST Intra Router One + + default_route: + next_hop_ip_address: 192.168.150.1 # ST Public Firewall Internal Port + acl: + 5: + action: PERMIT + + - hostname: ST_INTRA-PRV-RT-DR-1 + type: router + ports: + 1: + ip_address: 192.168.160.2 + subnet_mask: *ST_INTRA_DR_ONE_SUBNET + 2: + ip_address: 192.168.230.1 + subnet_mask: *ST_PROJ_A_SUBNET + 3: + ip_address: 192.168.240.1 + subnet_mask: *ST_PROJ_B_SUBNET + 4: + ip_address: 192.168.250.1 + subnet_mask: *ST_PROJ_C_SUBNET + default_route: + next_hop_ip_address: 192.168.160.1 # ST Intranet CR Router Port 2 + acl: + 5: + action: PERMIT + + - hostname: ST_INTRA-PRV-RT-DR-2 + type: router + default_gateway: 192.168.170.1 + ports: + 1: + ip_address: 192.168.170.2 + subnet_mask: *ST_INTRA_DR_TWO_SUBNET + 2: + ip_address: 192.168.200.1 + subnet_mask: *ST_HO_SUBNET + 3: + ip_address: 192.168.210.1 + subnet_mask: *ST_HR_SUBNET + default_route: + next_hop_ip_address: 192.168.170.1 # ST Intranet CR Router Port 3 + acl: + 5: + action: PERMIT + + ################################ + # SOME_TECH HEAD OFFICE SUBNET # + ################################ + + - hostname: ST_HO-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_HO-PRV-PC-1 + type: computer + ip_address: 192.168.200.2 + default_gateway: 192.168.200.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HO-PRV-PC-2 + type: computer + ip_address: 192.168.200.3 + default_gateway: 192.168.200.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HO-PRV-PC-3 + type: computer + ip_address: 192.168.200.4 + default_gateway: 192.168.200.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + #################################### + # SOME_TECH HUMAN RESOURCES SUBNET # + #################################### + + - hostname: ST_HR-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_HR-PRV-PC-1 + type: computer + ip_address: 192.168.210.2 + default_gateway: 192.168.210.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HR-PRV-PC-2 + type: computer + ip_address: 192.168.210.3 + default_gateway: 192.168.210.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_HR-PRV-PC-3 + type: computer + ip_address: 192.168.210.4 + default_gateway: 192.168.210.1 # ST_INTRA-PRV-RT-DR-2 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ######################### + # SOME_TECH DATA SUBNET # + ######################### + + - hostname: ST_DATA-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_DATA-PRV-SRV-STORAGE + type: server + ip_address: *ST_SRV_STORAGE_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_STORAGE_CONFIG + + - hostname: ST_DATA-PRV-SRV-DB + type: server + ip_address: *ST_SRV_DB_IP + subnet_mask: *ST_DATA_SUBNET + default_gateway: 192.168.220.1 + dns_server: *PUBLIC_DNS_IP + services: + *ST_SRV_DB_CONFIG + + ####################### + # SOME_TECH PROJECT A # + ####################### + + - hostname: ST_PROJ-A-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_PROJ-A-PRV-PC-1 + type: computer + ip_address: 192.168.230.2 + default_gateway: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-A-PRV-PC-2 + type: computer + ip_address: 192.168.230.3 + default_gateway: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-A-PRV-PC-3 + type: computer + ip_address: 192.168.230.4 + default_gateway: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1 (Port 2) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT B # + ####################### + + - hostname: ST_PROJ-B-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_PROJ-B-PRV-PC-1 + type: computer + ip_address: 192.168.240.2 + default_gateway: 192.168.240.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-B-PRV-PC-2 + type: computer + ip_address: 192.168.240.3 + default_gateway: 192.168.240.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-B-PRV-PC-3 + type: computer + ip_address: 192.168.240.4 + default_gateway: 192.168.240.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + ####################### + # SOME_TECH PROJECT C # + ####################### + + - hostname: ST_PROJ-C-PRV-SW-AS + type: switch + num_ports: 5 + + - hostname: ST_PROJ-C-PRV-PC-1 + type: computer + ip_address: 192.168.250.2 + default_gateway: 192.168.250.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-C-PRV-PC-2 + type: computer + ip_address: 192.168.250.3 + default_gateway: 192.168.250.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + - hostname: ST_PROJ-C-PRV-PC-3 + type: computer + ip_address: 192.168.250.4 + default_gateway: 192.168.250.1 # ST_INTRA-PRV-RT-DR-1 (Port 3) + dns_server: *PUBLIC_DNS_IP + applications: + *PC_DEFAULT_CONFIG + + + ############################## + # Simulation Network Links # + ############################## + links: + + ############################ + # HOME OFFICE SUBNET LINKS # + ############################ + + # Home Switch (Port 1) --> Home Router (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 1 + + # Home Switch (Port 2) --> PC 1 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: HOME-PUB-PC-1 + endpoint_b_port: 1 + + # Home Switch (Port 3) --> PC 2 (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: HOME-PUB-PC-2 + endpoint_b_port: 1 + + # Home Switch (Port 4) --> PC SRV (Port 1) + - endpoint_a_hostname: HOME-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: HOME-PUB-SRV + endpoint_b_port: 1 + + ################## + # Internet Links # + ################## + + # Internet Router (Port 1) --> Home Router (Port 2) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 1 + endpoint_b_hostname: HOME-PUB-RT-DR + endpoint_b_port: 2 + + # Internet Router (Port 2) --> DNS Server (Port 1) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 2 + endpoint_b_hostname: ISP-PUB-SRV-DNS + endpoint_b_port: 1 + + # Internet Router (Port 3) --> Remote Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-FW + endpoint_b_port: 1 + + #################### + # Remote DMZ Links # + #################### + + # Remote Firewall (Internal Port) --> Remote Site Router (Port 1) + - endpoint_a_hostname: REM-PUB-FW + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-RT-DR + endpoint_b_port: 1 + + #################### + # Remote Site Link # + #################### + + # Remote Site Router (Port 2) --> Remote Site Switch (Port 1) + - endpoint_a_hostname: REM-PUB-RT-DR + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-SW-AS + endpoint_b_port: 1 + + # Remote Site Switch (Port 2) --> Remote Site PC 1 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: REM-PUB-PC-1 + endpoint_b_port: 1 + + # Remote Site Switch (Port 3) --> Remote Site PC 2 (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: REM-PUB-PC-2 + endpoint_b_port: 1 + + # Remote Site Switch (Port 4) --> Remote Site Server (Port 1) + - endpoint_a_hostname: REM-PUB-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: REM-PUB-SRV + endpoint_b_port: 1 + + ####################### + # SOME_TECH DMZ Links # + ####################### + + # Internet Router (Port 4) --> Some Tech DMZ Firewall (External Port) + - endpoint_a_hostname: ISP-PUB-RT-BR + endpoint_a_port: 4 + endpoint_b_hostname: ST_PUB-FW + endpoint_b_port: 1 + + # Some Tech DMZ Firewall (DMZ Port) --> Some Tech Web Server (Port 1) + - endpoint_a_hostname: ST_PUB-FW + endpoint_a_port: 3 + endpoint_b_hostname: ST_DMZ-PUB-SRV-WEB + endpoint_b_port: 1 + + ############################ + # SOME_TECH INTRANET Links # + ############################ + + # Some Tech Intranet CR Router (Port 1) --> Some Tech DMZ Firewall (Internal Port) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 1 + endpoint_b_hostname: ST_PUB-FW + endpoint_b_port: 2 + + # Some Tech Intranet CR Router (Port 2) --> Some Tech Intranet DR Router 1 (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 2 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_b_port: 1 + + # Some Tech Intranet CR Router (Port 3) --> Some Tech Intranet DR Router 2 (Port 2) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 3 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-2 + endpoint_b_port: 1 + + # Some Tech Intranet Private Router CR (Port 4) --> Some Tech Data Private Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-CR + endpoint_a_port: 4 + endpoint_b_hostname: ST_DATA-PRV-SW-AS + endpoint_b_port: 1 + + + ############################### + # SOME_TECH HEAD OFFICE Links # + ############################### + + # Some Tech Head Office Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 2) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-2 + endpoint_b_port: 2 + + # Some Tech Head Office Switch (Port 2) --> Some Tech Head Office PC 1 (Port 1) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_HO-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 3) --> Some Tech Head Office PC 2 (Port 1) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_HO-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Head Office Switch (Port 4) --> Some Tech Head Office PC 3 (Port 1) + - endpoint_a_hostname: ST_HO-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_HO-PRV-PC-3 + endpoint_b_port: 1 + + + ################################### + # SOME_TECH HUMAN RESOURCES Links # + ################################### + + # Some Tech Human Resources Switch (Port 1) --> Some Tech Intranet Private Router DR 2 (Port 3) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 1 + endpoint_b_hostname: ST_INTRA-PRV-RT-DR-2 + endpoint_b_port: 3 + + # Some Tech Human Resources Switch (Port 2) --> Some Tech Human Resources PC 1 (Port 1) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_HR-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 3) --> Some Tech Human Resources PC 2 (Port 1) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_HR-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Human Resources Switch (Port 4) --> Some Tech Human Resources PC 3 (Port 1) + - endpoint_a_hostname: ST_HR-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_HR-PRV-PC-3 + endpoint_b_port: 1 + + ######################## + # SOME_TECH DATA Links # + ######################## + + # Some Tech Data Switch (Port 2) --> Some Tech Data Private Storage Server (Port 1) + - endpoint_a_hostname: ST_DATA-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_DATA-PRV-SRV-STORAGE + endpoint_b_port: 1 + + # Some Tech Data Switch (Port 3) --> Some Tech Data Private Database Server (Port 1) + + - endpoint_a_hostname: ST_DATA-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_DATA-PRV-SRV-DB + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT A Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 2) --> Some Tech Private Project A Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 2) --> Some Tech Project A PC 1 + - endpoint_a_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-A-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 3) --> Some Tech Project A PC 2 + - endpoint_a_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-A-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project A Switch (Port 4) --> Some Tech Project A PC 3 + - endpoint_a_hostname: ST_PROJ-A-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-A-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT B Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 3) --> Some Tech Private Project B Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 2) --> Some Tech Project B PC 1 + - endpoint_a_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-B-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 3) --> Some Tech Project B PC 2 + - endpoint_a_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-B-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project B Switch (Port 4) --> Some Tech Project B PC 3 + - endpoint_a_hostname: ST_PROJ-B-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-B-PRV-PC-3 + endpoint_b_port: 1 + + ############################# + # SOME_TECH PROJECT C Links # + ############################# + + # Some Tech Intranet Private Router DR 1 (Port 4) --> Some Tech Private Project C Switch (Port 1) + - endpoint_a_hostname: ST_INTRA-PRV-RT-DR-1 + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 2) --> Some Tech Project C PC 1 + - endpoint_a_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_a_port: 2 + endpoint_b_hostname: ST_PROJ-C-PRV-PC-1 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 3) --> Some Tech Project C PC 2 + - endpoint_a_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_a_port: 3 + endpoint_b_hostname: ST_PROJ-C-PRV-PC-2 + endpoint_b_port: 1 + + # Some Tech Private Project C Switch (Port 4) --> Some Tech Project C PC 3 + - endpoint_a_hostname: ST_PROJ-C-PRV-SW-AS + endpoint_a_port: 4 + endpoint_b_hostname: ST_PROJ-C-PRV-PC-3 + endpoint_b_port: 1 + +################################## +# Use Case 7 Agent YAML Anchors # +################################## + +############################## +# Green Agent YAML Anchors # +############################## + +# Green Agent Reward Impacts Values # + +LOW_WEIGHT_IMPACT: &LOW_WEIGHT_IMPACT 0.2 +MEDIUM_WEIGHT_IMPACT: &MEDIUM_WEIGHT_IMPACT 0.5 +HIGH_WEIGHT_IMPACT: &HIGH_WEIGHT_IMPACT 0.95 + +LOW_WEIGHT_IMPACT_NEGATIVE: &LOW_WEIGHT_IMPACT_NEG -0.2 +MEDIUM_WEIGHT_IMPACT_NEGATIVE: &MEDIUM_WEIGHT_IMPACT_NEG -0.5 +HIGH_WEIGHT_IMPACT_NEGATIVE: &HIGH_WEIGHT_IMPACT_NEG -0.8 + +# Default Green Agent Action Space Configuration Anchor # + +DEFAULT_GREEN_AGENT_MAX_EXECUTIONS: &DEFAULT_GREEN_AGENT_MAX_EXECUTIONS 1000 # Ensures green agent activity through-out an episode + +################################################# +# Probabilistic Green Agent Config Yaml Anchors # +################################################# + +# Probabilistic Green Agent | 20% node-application-execute | 80% do-nothing # +PROBABILISTIC_CONFIG_20_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_20 + action_probabilities: + 0: 0.8 + 1: 0.2 + +# Probabilistic Green Agent | 40% node-application-execute | 60% do-nothing # +PROBABILISTIC_CONFIG_40_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_40 + action_probabilities: + 0: 0.6 + 1: 0.4 + +# Probabilistic Green Agent | 60% node-application-execute | 40% do-nothing # +PROBABILISTIC_CONFIG_60_PERCENTAGE_PROBABILITY: &GREEN_PROBABILISTIC_60 + action_probabilities: + 0: 0.4 + 1: 0.6 + + +# System Green Agent Config UC7 Network Wide Yaml Anchor # + +# Lists the IP_Address of all hosts that contain DNS and NTP Service Clients +UC7_IP_LIST: &UC7_IP_LIST + # ====== Home Office ======== + - 192.168.1.2 # HOME-PUB-PC-1 | ip_id: 2 + - 192.168.1.3 # HOME-PUB-PC-2 | ip_id: 3 + - 192.168.1.4 # HOME-PUB-PC-SRV | ip_id: 4 + # ====== Remote Site ======== + - 192.168.20.2 # REM-PUB-PC-1 | ip_id: 5 + - 192.168.20.3 # REM-PUB-PC-2 | ip_id: 6 + - 192.168.20.4 # REM-PUB-SRV | ip_id: 7 + # ====== ST Public DMZ ======= + - *ST_PUB_SRV_WEB_IP # 192.168.100.2 (ST_DMZ-PUB-SRV-WEB) | ip_id: 8 + # ====== ST Head Office ======= + - 192.168.200.2 # ST_HO-PRV-PC-1 | ip_id: 9 + - 192.168.200.3 # ST_HO-PRV-PC-2 | ip_id: 10 + - 192.168.200.4 # ST_HO-PRV-PC-3 | ip_id: 11 + # ===== ST Human Resources ====== + - 192.168.210.2 # ST_HR-PRV-PC-1 | ip_id: 12 + - 192.168.210.3 # ST_HR-PRV-PC-2 | ip_id: 13 + - 192.168.210.4 # ST_HR-PRV-PC-3 | ip_id: 14 + # ====== ST DATA Servers ======= + - *ST_SRV_STORAGE_IP # 192.168.220.2 (ST_DATA-PRV-SRV-STORAGE) | ip_id: 15 + - *ST_SRV_DB_IP # 192.168.220.3 (ST_DATA-PRV-SRV-DB) | ip_id: 16 + # ====== ST Project A ======= + - 192.168.230.2 # PROJ-A-PRV-PC-1 | ip_id: 17 + - 192.168.230.3 # PROJ-A-PRV-PC-2 | ip_id: 18 + - 192.168.230.4 # PROJ-A-PRV-PC-3 | ip_id: 19 + # ====== ST Project B ======= + - 192.168.240.2 # PROJ-B-PRV-PC-1 | ip_id: 20 + - 192.168.240.3 # PROJ-B-PRV-PC-2 | ip_id: 21 + - 192.168.240.4 # PROJ-B-PRV-PC-3 | ip_id: 22 + # ====== ST Project C ======= + - 192.168.250.2 # PROJ-C-PRV-PC-1 | ip_id: 23 + - 192.168.250.3 # PROJ-C-PRV-PC-2 | ip_id: 24 + - 192.168.250.4 # PROJ-C-PRV-PC-3 | ip_id: 25 + +############################################ +# Use Case 7 Agent Configuration Section # +############################################ + +agents: + ####################################################### + # UC7 Green Agents Path of Life (POL) Configuration # + ####################################################### + + + #################################### + # Home Office Network POL Config # + #################################### + + # Home Office Green Agent Pattern Of Life + # ====================================== + # 1. Three Home workers accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. Three Home workers accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: HOME_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-1"] + target_application: "database-client" + start_step: 4 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-1 + + - ref: HOME_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["HOME-PUB-PC-2"] + target_application: "database-client" + start_step: 8 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + - ref: HOME_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: HOME-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: HOME-PUB-PC-2 + + #################################### + # Remote Site Network POL Config # + #################################### + + # Remote Site Green Agent Pattern Of Life + # ====================================== + # 1. Three Remote workers accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. Three Remote workers accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: REMOTE_WORKER-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-1"] + target_application: "database-client" + start_step: 12 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-1 + + - ref: REMOTE_WORKER-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["REM-PUB-PC-2"] + target_application: "database-client" + start_step: 16 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + - ref: REMOTE_WORKER-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: REM-PUB-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: REM-PUB-PC-2 + + #################################### + # ST Project A Network POL Config # + #################################### + + # ST Project A Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + + - ref: PROJ_A-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-A-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-1 + + - ref: PROJ_A-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-1 + + - ref: PROJ_A-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-A-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-A-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-2 + + - ref: PROJ_A-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-A-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-3 + + - ref: PROJ_A-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-A-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-A-PRV-PC-3 + + #################################### + # ST Project B Network POL Config # + #################################### + + # ST Project B Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: PROJ_B-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-B-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-1 + + - ref: PROJ_B-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-B-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-1 + + - ref: PROJ_B-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-B-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-2 + + - ref: PROJ_B-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-B-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-3 + + - ref: PROJ_B-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-B-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-B-PRV-PC-3 + + #################################### + # ST Project C Network POL Config # + #################################### + + # ST Project C Green Agent Pattern Of Life + # ======================================== + # 1. A Senior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 2. A Senior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. Two Junior Developer accessing the SOME_TECH Private Database (ST_DATA-PRV-SRV-DB) + # 4. Two Junior Developer accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: PROJ_C-SENIOR-DEV-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-C-PRV-PC-1"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-1 + + - ref: PROJ_C-SENIOR-DEV-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-C-PRV-PC-1 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_40 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-1 + + - ref: PROJ_C-JUNIOR-DEV-1-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-C-PRV-PC-2"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-1-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-C-PRV-PC-2 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-2 + + - ref: PROJ_C-JUNIOR-DEV-2-DB + team: GREEN + type: periodic-agent + observation_space: {} + agent_settings: + possible_start_nodes: ["ST_PROJ-C-PRV-PC-3"] + target_application: "database-client" + start_step: 1 + start_variance: 1 + max_executions: *DEFAULT_GREEN_AGENT_MAX_EXECUTIONS + frequency: 4 + variance: 1 + reward_function: + reward_components: + - type: green-admin-database-unreachable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-3 + + - ref: PROJ_C-JUNIOR-DEV-2-WEB + team: GREEN + type: probabilistic-agent + observation_space: {} + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: web-browser + agent_settings: + <<: *GREEN_PROBABILISTIC_20 + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *LOW_WEIGHT_IMPACT + options: + node_hostname: ST_PROJ-C-PRV-PC-3 + + ###################################### + # ST Head Office Network POL Config # + ###################################### + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. The ST CEO accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 2. The ST CTO accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 3. The ST CFO accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: CEO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HO-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST_HO-PRV-PC-1 + + - ref: CTO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HO-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HO-PRV-PC-2 + + - ref: CFO + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HO-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HO-PRV-PC-3 + + ########################################## + # ST Human Resources Network POL Config # + ########################################## + + # ST Head Office Green Agent Pattern Of Life + # ========================================== + # 1. A senior HR staff accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + # 2. Two junior HR staff accessing the SOME_TECH Public web-server (ST_DMZ-PUB-SRV-WEB) + + - ref: SENIOR_HR + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HR-PRV-PC-1 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HR-PRV-PC-1 + + - ref: JUNIOR_HR-1 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HR-PRV-PC-2 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HR-PRV-PC-2 + + - ref: JUNIOR_HR-2 + team: GREEN + type: probabilistic-agent + observation_space: {} + agent_settings: + <<: *GREEN_PROBABILISTIC_60 + action_space: + action_map: + 0: + action: do-nothing + options: {} + 1: + action: node-application-execute + options: + node_name: ST_HR-PRV-PC-3 + application_name: web-browser + reward_function: + reward_components: + - type: webpage-unavailable-penalty + weight: *MEDIUM_WEIGHT_IMPACT + options: + node_hostname: ST_HR-PRV-PC-3 + + ########################## + # UC7 Red Agent Config # + ########################## + + - *red +########################### +# UC7 Blue Agent Config # +########################### + + - ref: defender + team: BLUE + type: proxy-agent + observation_space: + type: custom + options: + components: + - type: nodes + label: NODES + options: + hosts: + # TAP001 Potential Starting Note | ST_PROJ-A-PRV-PC-1 + - hostname: ST_PROJ-A-PRV-PC-1 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST_PROJ-B-PRV-PC-2 + - hostname: ST_PROJ-B-PRV-PC-2 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # TAP001 Potential Starting Note | ST_PROJ-C-PRV-PC-3 + - hostname: ST_PROJ-C-PRV-PC-3 + services: + - service_name: ftp-client + applications: + - application_name: ransomware-script + - application_name: database-client + folders: + - folder_name: downloads + files: + - file_name: malware_dropper.ps1 + - folder_name: exfiltration_folder + files: + - file_name: database.db + # ST DATA Server Database + - hostname: ST_DATA-PRV-SRV-DB + folders: + - folder_name: database + files: + - file_name: database.db + routers: + - hostname: ST_INTRA-PRV-RT-CR # TAP003 Malicious ACL Target | ROUTER0 + - hostname: ST_INTRA-PRV-RT-DR-1 # TAP003 Malicious ACL Target | ROUTER1 + - hostname: REM-PUB-RT-DR # TAP003 Malicious ACL Target | ROUTER2 + num_ports: 5 + num_services: 2 + num_applications: 2 + num_folders: 2 + num_files: 1 + num_nics: 1 + ip_list: + # ip_list is indexed at 2: + # 0 reserved for padding to align with observations + # 1 reserved for ALL ips + *UC7_IP_LIST + wildcard_list: + - 0.0.0.1 # 0 + - 0.0.0.255 # 1 + - 0.0.255.255 # 2 + port_list: + # 0 is a padding placeholder + # 1 means ALL ports + - FTP # 2 + - DNS # 3 + - HTTP # 4 + - NTP # 5 + - POSTGRES_SERVER # 6 + - SSH # 7 + + protocol_list: + # 0 is padding placeholder + # 1 means ALL protocols + - ICMP # 2 + - TCP # 3 + - UDP # 4 + num_rules: 5 + include_num_access: true + include_nmne: true + monitored_traffic: + tcp: + - HTTP + - POSTGRES_SERVER + icmp: + - NONE + - type: links + label: LINKS + options: + link_references: + # HOME OFFICE SUBNET LINKS + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-RT-DR:eth-1 # 1 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-1:eth-1 # 2 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-PC-2:eth-1 # 3 + - HOME-PUB-SW-AS:eth-1<->HOME-PUB-SRV:eth-1 # 4 + # Internet LINKS + - ISP-PUB-RT-BR:eth-1<->HOME-PUB-RT-DR:eth-2 # 5 + - ISP-PUB-RT-BR:eth-2<->ISP-PUB-SRV-DNS:eth-1 # 6 + - ISP-PUB-RT-BR:eth-3<->REM-PUB-FW:eth-1 # 7 + # Remote DMZ Links + - REM-PUB-FW:eth-2<->REM-PUB-RT-DR:eth-1 # 8 + # Remote Site Links + - REM-PUB-RT-DR:eth-2<->REM-PUB-SW-AS:eth-1 # 9 + - REM-PUB-SW-AS:eth-2<->REM-PUB-PC-1:eth-1 # 10 + - REM-PUB-SW-AS:eth-3<->REM-PUB-PC-2:eth-1 # 11 + - REM-PUB-SW-AS:eth-4<->REM-PUB-SRV:eth-1 # 12 + # SOME_TECH DMZ + - ISP-PUB-RT-BR:eth-4<->ST_PUB-FW:eth-1 # 13 + - ST_PUB-FW:eth-3<->ST_DMZ-PUB-SRV-WEB:eth-1 # 14 + # SOME_TECH Intranet + - ST_INTRA-PRV-RT-CR:eth-1<->ST_PUB-FW:eth-2 # 15 + - ST_INTRA-PRV-RT-CR:eth-2<->ST_INTRA-PRV-RT-DR-1:eth-1 # 16 + - ST_INTRA-PRV-RT-CR:eth-3<->ST_INTRA-PRV-RT-DR-2:eth-1 # 17 + - ST_INTRA-PRV-RT-CR:eth-4<->ST_DATA-PRV-SW-AS:eth-1 # 18 + # SOME_TECH Head Office + - ST_HO-PRV-SW-AS:eth-1<->ST_INTRA-PRV-RT-DR-2:eth-2 # 19 + - ST_HO-PRV-SW-AS:eth-2<->ST_HO-PRV-PC-1:eth-1 # 20 + - ST_HO-PRV-SW-AS:eth-3<->ST_HO-PRV-PC-2:eth-1 # 21 + - ST_HO-PRV-SW-AS:eth-4<->ST_HO-PRV-PC-3:eth-1 # 22 + # SOME_TECH Human Resources + - ST_HR-PRV-SW-AS:eth-1<->ST_INTRA-PRV-RT-DR-2:eth-3 # 23 + - ST_HR-PRV-SW-AS:eth-2<->ST_HR-PRV-PC-1:eth-1 # 24 + - ST_HR-PRV-SW-AS:eth-3<->ST_HR-PRV-PC-2:eth-1 # 25 + - ST_HR-PRV-SW-AS:eth-4<->ST_HR-PRV-PC-3:eth-1 # 26 + # SOME_TECH Data Links + - ST_DATA-PRV-SW-AS:eth-2<->ST_DATA-PRV-SRV-STORAGE:eth-1 # 27 + - ST_DATA-PRV-SW-AS:eth-3<->ST_DATA-PRV-SRV-DB:eth-1 # 28 + # SOME_TECH Project A Links + - ST_INTRA-PRV-RT-DR-1:eth-2<->ST_PROJ-A-PRV-SW-AS:eth-1 # 29 + - ST_PROJ-A-PRV-SW-AS:eth2<->ST_PROJ-A-PRV-PC-1:eth-1 # 31 + - ST_PROJ-A-PRV-SW-AS:eth3<->ST_PROJ-A-PRV-PC-2:eth-1 # 32 + - ST_PROJ-A-PRV-SW-AS:eth4<->ST_PROJ-A-PRV-PC-3:eth-1 # 33 + # SOME_TECH Project B Links + - ST_INTRA-PRV-RT-DR-1:eth-3<->ST_PROJ-B-PRV-SW-AS:eth-1 # 34 + - ST_PROJ-B-PRV-SW-AS:eth2<->ST_PROJ-B-PRV-PC-1:eth-1 # 35 + - ST_PROJ-B-PRV-SW-AS:eth3<->ST_PROJ-B-PRV-PC-2:eth-1 # 36 + - ST_PROJ-B-PRV-SW-AS:eth4<->ST_PROJ-B-PRV-PC-3:eth-1 # 37 + # SOME_TECH Project C Links + - ST_INTRA-PRV-RT-DR-1:eth-4<->ST_PROJ-C-PRV-SW-AS:eth-1 # 38 + - ST_PROJ-A-PRV-SW-AS:eth2<->ST_PROJ-C-PRV-PC-1:eth-1 # 39 + - ST_PROJ-A-PRV-SW-AS:eth3<->ST_PROJ-C-PRV-PC-2:eth-1 # 40 + - ST_PROJ-A-PRV-SW-AS:eth4<->ST_PROJ-C-PRV-PC-3:eth-1 # 41 + action_space: + action_map: + 0: + action: do-nothing + options: {} + + # |======================================| + # | ST_PROJ-A-PRV-PC-1 | + # |======================================| + + # ST_PROJ-A-PRV-PC-1 | node-os-scan + 1: + action: node-os-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | node-shutdown + 2: + action: node-shutdown + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | node-startup + 3: + action: node-startup + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | node-reset + 4: + action: node-reset + options: + node_name: ST_PROJ-A-PRV-PC-1 + # ST_PROJ-A-PRV-PC-1 | host-nic-disable + 5: + action: host-nic-disable + options: + node_name: ST_PROJ-A-PRV-PC-1 + nic_num: 0 + # ST_PROJ-A-PRV-PC-1 | host-nic-enable + 6: + action: host-nic-enable + options: + node_name: ST_PROJ-A-PRV-PC-1 + nic_num: 0 + # ST_PROJ-A-PRV-PC-1 | node-application-close | database-client + 7: + action: node-application-close + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + # ST_PROJ-A-PRV-PC-1 | node-application-scan | database-client + 8: + action: node-application-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + # ST_PROJ-A-PRV-PC-1 | node-application-fix | database-client + 9: + action: node-application-fix + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + # ST_PROJ-A-PRV-PC-1 | node-application-remove | database-client + 10: + action: node-application-remove + options: + node_name: ST_PROJ-A-PRV-PC-1 + application_name: database-client + + + # ST_PROJ-A-PRV-PC-1 | node-file-scan | downloads/malware_dropper.ps1 + 11: + action: node-file-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST_PROJ-A-PRV-PC-1 | node-file-scan | exfiltration_folder/database.db + 12: + action: node-file-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + file_name: database.db + + # ST_PROJ-A-PRV-PC-1 | node-folder-scan | downloads/ + 13: + action: node-folder-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: downloads + + # ST_PROJ-A-PRV-PC-1 | node-folder-scan | exfiltration_folder/ + 14: + action: node-folder-scan + options: + node_name: ST_PROJ-A-PRV-PC-1 + folder_name: exfiltration_folder + + # |======================================| + # | ST_PROJ-B-PRV-PC-2 | + # |======================================| + + # ST_PROJ-B-PRV-PC-2 | node-os-scan + 15: + action: node-os-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | node-shutdown + 16: + action: node-shutdown + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | node-startup + 17: + action: node-startup + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | node-reset + 18: + action: node-reset + options: + node_name: ST_PROJ-B-PRV-PC-2 + + # ST_PROJ-B-PRV-PC-2 | host-nic-disable + 19: + action: host-nic-disable + options: + node_name: ST_PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST_PROJ-B-PRV-PC-2 | host-nic-enable + 20: + action: host-nic-enable + options: + node_name: ST_PROJ-B-PRV-PC-2 + nic_num: 0 + + # ST_PROJ-B-PRV-PC-2 | node-application-close | database-client + 21: + action: node-application-close + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-application-scan | database-client + 22: + action: node-application-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-application-fix | database-client + 23: + action: node-application-fix + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-application-remove | database-client + 24: + action: node-application-remove + options: + node_name: ST_PROJ-B-PRV-PC-2 + application_name: database-client + + # ST_PROJ-B-PRV-PC-2 | node-file-scan | downloads/malware_dropper.ps1 + 25: + action: node-file-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST_PROJ-B-PRV-PC-2 | node-file-scan | exfiltration_folder/database.db + 26: + action: node-file-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + file_name: database.db + + # ST_PROJ-B-PRV-PC-2 | node-folder-scan | downloads/ + 27: + action: node-folder-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: downloads + + # ST_PROJ-B-PRV-PC-2 | node-folder-scan | exfiltration_folder/ + 28: + action: node-folder-scan + options: + node_name: ST_PROJ-B-PRV-PC-2 + folder_name: exfiltration_folder + + # |======================================| + # | ST_PROJ-C-PRV-PC-3 | + # |======================================| + + # ST_PROJ-C-PRV-PC-3 | node-os-scan + 29: + action: node-os-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | node-shutdown + 30: + action: node-shutdown + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | node-startup + 31: + action: node-startup + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | node-reset + 32: + action: node-reset + options: + node_name: ST_PROJ-C-PRV-PC-3 + + # ST_PROJ-C-PRV-PC-3 | host-nic-disable + 33: + action: host-nic-disable + options: + node_name: ST_PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST_PROJ-C-PRV-PC-3 | host-nic-enable + 34: + action: host-nic-enable + options: + node_name: ST_PROJ-C-PRV-PC-3 + nic_num: 0 + + # ST_PROJ-C-PRV-PC-3 | node-application-close | database-client + 35: + action: node-application-close + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-application-scan | database-client + 36: + action: node-application-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-application-fix | database-client + 37: + action: node-application-fix + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-application-remove | database-client + 38: + action: node-application-remove + options: + node_name: ST_PROJ-C-PRV-PC-3 + application_name: database-client + + # ST_PROJ-C-PRV-PC-3 | node-file-scan | downloads/malware_dropper.ps1 + 39: + action: node-file-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: downloads + file_name: malware_dropper.ps1 + + # ST_PROJ-C-PRV-PC-3 | node-file-scan | exfiltration_folder/database.db + 40: + action: node-file-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + file_name: database.db + + # ST_PROJ-C-PRV-PC-3 | node-folder-scan | downloads/ + 41: + action: node-folder-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: downloads + + # ST_PROJ-C-PRV-PC-3 | node-folder-scan | exfiltration_folder/ + 42: + action: node-folder-scan + options: + node_name: ST_PROJ-C-PRV-PC-3 + folder_name: exfiltration_folder + + # |======================================| + # | ST_INTRA-PRV-RT-CR | + # |======================================| + + # ST_INTRA-PRV-RT-CR | router-acl-add-rule | P2: ST_PROJ-A-PRV-PC-1 !==> ST_DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 43: + action: router-acl-add-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST_PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST_DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST_INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 1 + 44: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 1 + + # ST_INTRA-PRV-RT-CR | router-acl-add-rule | P3: ST_PROJ-B-PRV-PC-2 !==> ST_DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 45: + action: router-acl-add-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 2 + permission: DENY + src_ip: 192.168.240.3 # (ST_PROJ-B-PRV-PC-2) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST_DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + # ST_INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 2 + 46: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 2 + + # ST_INTRA-PRV-RT-CR | router-acl-add-rule | P4: ST_PROJ-C-PRV-PC-3 !==> ST_DATA-PRV-SRV-DB (TCP:POSTGRES_SERVER) + 47: + action: router-acl-add-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 3 + permission: DENY + src_ip: 192.168.250.4 # (ST_PROJ-C-PRV-PC-3) + src_wildcard: 0.0.255.255 + src_port: POSTGRES_SERVER + dst_ip: 192.168.220.3 # (ST_DATA-PRV-SRV-DB) + dst_wildcard: 0.0.255.255 + dst_port: POSTGRES_SERVER + protocol_name: TCP + + # ST_INTRA-PRV-RT-CR | REMOVE_ACL_ADDRULE | Removes a given ACL at position 3 + 48: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-CR + position: 3 + + # |======================================| + # | ST_DATA-PRV-SRV-DB | + # |======================================| + + # ST_DATA-PRV-SRV-DB | node-file-scan | Scans the database.db file (health status) + 49: + action: node-file-scan + options: + node_name: ST_DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # ST_DATA-PRV-SRV-DB | node-account-change-password | Changes the password of a user account + 50: + action: node-account-change-password + options: + node_name: ST_DATA-PRV-SRV-DB + username: admin # default account + current_password: admin # default password + new_password: thr33_alert_wolv3z # A more 'secure' password + + # |======================================| + # | ST_INTRA-PRV-RT-DR-1 | + # |======================================| + + # ST_INTRA-PRV-RT-DR-1 | router-acl-add-rule | P1: ST_INTRA-PRV-RT-DR-1 !==> ANY (TCP:SSH) + 51: + action: router-acl-add-rule + options: + target_router: ST_INTRA-PRV-RT-DR-1 + position: 1 + permission: DENY + src_ip: 192.168.230.2 # (ST_PROJ-A-PRV-PC-1) + src_wildcard: 0.0.255.255 + src_port: SSH + dst_ip: ALL + dst_wildcard: 0.0.255.255 + dst_port: SSH + protocol_name: TCP + + # ST_INTRA-PRV-RT-DR-1 | node-account-change-password + 52: + action: node-account-change-password + options: + node_name: ST_INTRA-PRV-RT-DR-1 + username: admin + current_password: admin + new_password: secure_password + + # ST_INTRA-PRV-RT-DR-1 | router-acl-remove-rule | Removes the given ACL at position 1 + 53: + action: router-acl-remove-rule + options: + target_router: ST_INTRA-PRV-RT-DR-1 + position: 1 + + # |======================================| + # | REM-PUB-RT-DR | + # |======================================| + + # REM-PUB-RT-DR | node-account-change-password + 54: + action: node-account-change-password + options: + node_name: REM-PUB-RT-DR + username: admin + current_password: admin + new_password: secure_password + + # REM-PUB-RT-DR | router-acl-remove-rule | Removes the given ACL at position 1 + 55: + action: router-acl-remove-rule + options: + target_router: REM-PUB-RT-DR + position: 1 + + reward_function: + reward_components: + - type: database-file-integrity + weight: *HIGH_WEIGHT_IMPACT + options: + node_hostname: ST_DATA-PRV-SRV-DB + folder_name: database + file_name: database.db + + # Home Site Green Agents (32 Green Agents each contributing 0.03125 of blue reward) + + # Blue Shared Reward | HOME_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-DB + + # Blue Shared Reward | HOME_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-1-WEB + + # Blue Shared Reward | HOME_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-DB + + - type: shared-reward + weight: 0.03125 + options: + agent_name: HOME_WORKER-2-WEB + + # Remote Site Green Agents + + # Blue Shared Reward | REMOTE_WORKER-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-DB + + # Blue Shared Reward | REMOTE_WORKER-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-1-WEB + + # Blue Shared Reward | REMOTE_WORKER-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-DB + + # Blue Shared Reward | REMOTE_WORKER-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: REMOTE_WORKER-2-WEB + + # ST Project A Green Agents + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_A-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_A-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_A-JUNIOR-DEV-2-WEB + + # ST Project B Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Project C Green Agents + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-DB + + # Blue Shared Reward | PROJ_B-SENIOR-DEV-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-SENIOR-DEV-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-1-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-1-WEB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-DB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-DB + + # Blue Shared Reward | PROJ_B-JUNIOR-DEV-2-WEB + - type: shared-reward + weight: 0.03125 + options: + agent_name: PROJ_B-JUNIOR-DEV-2-WEB + + # ST Head Office Green Agents (CEO/CFO/CTO) + + # Blue Shared Reward | CEO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CEO + + # Blue Shared Reward | CFO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CFO + + # Blue Shared Reward | CTO + - type: shared-reward + weight: 0.03125 + options: + agent_name: CTO + + # ST Human Resources Green Agents + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: SENIOR_HR + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-1 + + # Blue Shared Reward | SENIOR_HR + - type: shared-reward + weight: 0.03125 + options: + agent_name: JUNIOR_HR-2 + + agent_settings: + flatten_obs: False diff --git a/src/primaite/game/agent/actions/node.py b/src/primaite/game/agent/actions/node.py index b1b6ec12..5b62957a 100644 --- a/src/primaite/game/agent/actions/node.py +++ b/src/primaite/game/agent/actions/node.py @@ -37,15 +37,18 @@ class NodeAbstractAction(AbstractAction, ABC): return ["network", "node", config.node_name, config.verb] -class NodeOSScanAction(NodeAbstractAction, discriminator="node-os-scan"): +class NodeOSScanAction(AbstractAction, discriminator="node-os-scan"): """Action which scans a node's OS.""" - config: "NodeOSScanAction.ConfigSchema" + class ConfigSchema(AbstractAction.ConfigSchema, ABC): + """Base Configuration schema for Node actions.""" - class ConfigSchema(NodeAbstractAction.ConfigSchema): - """Configuration schema for NodeOSScanAction.""" + node_name: str - verb: ClassVar[str] = "scan" + @classmethod + def form_request(cls, config: ConfigSchema) -> RequestFormat: + """Return the action formatted as a request which can be ingested by the PrimAITE simulation.""" + return ["network", "node", config.node_name, "os", "scan"] class NodeShutdownAction(NodeAbstractAction, discriminator="node-shutdown"): diff --git a/src/primaite/game/agent/interface.py b/src/primaite/game/agent/interface.py index a6e9739f..b9a7fcc8 100644 --- a/src/primaite/game/agent/interface.py +++ b/src/primaite/game/agent/interface.py @@ -124,8 +124,8 @@ class AbstractAgent(BaseModel, ABC): pass else: # format dict by putting each key-value entry on a separate line and putting a blank line on the end. - param_string = "\n".join([*[f"{k}: {v:.30}" for k, v in item.parameters.items()], ""]) - data_string = "\n".join([*[f"{k}: {v:.30}" for k, v in item.response.data], ""]) + param_string = "\n".join([*[f"{k}: {str(v):.80}" for k, v in item.parameters.items()], ""]) + data_string = "\n".join([*[f"{k}: {str(v):.80}" for k, v in item.response.data.items()], ""]) table.add_row([item.timestep, item.action, param_string, item.response.status, data_string]) print(table) diff --git a/src/primaite/game/agent/observations/acl_observation.py b/src/primaite/game/agent/observations/acl_observation.py index b2f5e786..8a137629 100644 --- a/src/primaite/game/agent/observations/acl_observation.py +++ b/src/primaite/game/agent/observations/acl_observation.py @@ -65,8 +65,7 @@ class ACLObservation(AbstractObservation, discriminator="acl"): self.port_to_id: Dict[str, int] = {p: i + 2 for i, p in enumerate(port_list)} self.protocol_to_id: Dict[str, int] = {p: i + 2 for i, p in enumerate(protocol_list)} self.default_observation: Dict = { - i - + 1: { + i: { "position": i, "permission": 0, "source_ip_id": 0, @@ -94,12 +93,11 @@ class ACLObservation(AbstractObservation, discriminator="acl"): return self.default_observation obs = {} acl_items = dict(acl_state.items()) - i = 1 # don't show rule 0 for compatibility reasons. - while i < self.num_rules + 1: + for i in range(self.num_rules): rule_state = acl_items[i] if rule_state is None: obs[i] = { - "position": i - 1, + "position": i, "permission": 0, "source_ip_id": 0, "source_wildcard_id": 0, @@ -125,7 +123,7 @@ class ACLObservation(AbstractObservation, discriminator="acl"): protocol = rule_state["protocol"] protocol_id = self.protocol_to_id.get(protocol, 1) obs[i] = { - "position": i - 1, + "position": i, "permission": rule_state["action"], "source_ip_id": src_node_id, "source_wildcard_id": src_wildcard_id, @@ -135,7 +133,6 @@ class ACLObservation(AbstractObservation, discriminator="acl"): "dest_port_id": dst_port_id, "protocol_id": protocol_id, } - i += 1 return obs @property @@ -148,8 +145,7 @@ class ACLObservation(AbstractObservation, discriminator="acl"): """ return spaces.Dict( { - i - + 1: spaces.Dict( + i: spaces.Dict( { "position": spaces.Discrete(self.num_rules), "permission": spaces.Discrete(3), diff --git a/src/primaite/game/agent/scripted_agents/TAP001.py b/src/primaite/game/agent/scripted_agents/TAP001.py new file mode 100644 index 00000000..80f4af03 --- /dev/null +++ b/src/primaite/game/agent/scripted_agents/TAP001.py @@ -0,0 +1,1080 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK + +import random +from enum import Enum, IntEnum +from typing import Dict, List, Literal, Optional, Tuple, Type, Union + +from gymnasium.core import ObsType +from prettytable import MARKDOWN, PrettyTable +from pydantic import Field, PositiveInt + +from primaite.game.agent.interface import RequestResponse +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.utils.validation.ip_protocol import IPProtocol, PROTOCOL_LOOKUP +from primaite.utils.validation.ipv4_address import IPV4Address, StrIP +from primaite.utils.validation.port import Port, PORT_LOOKUP + + +class MobileMalwareKillChainOptions(KillChainOptions): + """Model Validation for the TAP001's implementation of the mobile malware kill chain.""" + + class _ActivateOptions(KillChainStageOptions): + pass + + class _PropagateOptions(KillChainStageOptions): + scan_attempts: PositiveInt = 1 + """The amount of scan actions the tap001 is permitted to take until the PROPAGATE stage is considered failed.""" + repeat_scan: bool = False + """Optional boolean flag to control repeat scan behaviour after an the initial scan.""" + network_addresses: List[str] + """An ordered list which contains the network addresses for the propagate step to follow. + + At current, Agents have no way of probing the simulation for routing information (Such as traceroute) + + Therefore, to propagate through the an network, a list of each network address must be + be provided to the TAP001 agent. The TAP001 agent will scan each address in + the sequential order. + """ + + class _CommandAndControlOptions(KillChainStageOptions): + keep_alive_frequency: PositiveInt = 5 + """The frequency of ``keep_alives`` that the C2 Beacon will be configured to use.""" + masquerade_port: Port = 80 + """The port that the C2 Beacon will be configured to use.""" + masquerade_protocol: IPProtocol = "tcp" + """The protocol that the C2 Beacon will be configured to use.""" + c2_server_name: str = "" + """The hostname of the C2_Server that the C2 Beacon is intended to use.""" + c2_server_ip: StrIP + + class _PayloadOptions(KillChainStageOptions): + payload: Optional[str] = "ENCRYPT" + """The query used on payload kill chain step. Defaults to Encrypt.""" + exfiltrate: bool = True + """Boolean which indicates if TAP001 should exfiltrate the target database.db file.""" + corrupt: bool = True + """Boolean which indicates if TAP001 should launch the RansomwareScript.""" + exfiltration_folder_name: Optional[str] = None + """The folder used to store the database.db file after successful exfiltration.""" + target_username: str = "admin" + """The username used to login into a target node in order to perform file exfiltration.""" + target_password: str = "admin" + """The password used to login into a target node in order to perform file exfiltration.""" + continue_on_failed_exfil: bool = True + """Whether TAP001 should continue to encrypt if the exfiltration fails.""" + + ACTIVATE: _ActivateOptions = Field(default_factory=lambda: MobileMalwareKillChainOptions._ActivateOptions()) + PROPAGATE: _PropagateOptions = Field(default_factory=lambda: MobileMalwareKillChainOptions._PropagateOptions()) + COMMAND_AND_CONTROL: _CommandAndControlOptions = Field( + default_factory=lambda: MobileMalwareKillChainOptions._CommandAndControlOptions() + ) + PAYLOAD: _PayloadOptions = Field(default_factory=lambda: MobileMalwareKillChainOptions._PayloadOptions()) + + +class MobileMalwareKillChain(IntEnum): + """ + Enumeration representing different attack stages of the mobile malware kill chain. + + This enumeration defines the various stages in the mobile malware kill chain + can be in during its lifecycle in the simulation. + Each stage represents a specific phase in the attack process. + """ + + DOWNLOAD = 1 + "Malware is downloaded onto the tap001's starting client." + INSTALL = 2 + "The malware is activated which initiates it's malicious functions." + ACTIVATE = 3 + "The malware installs itself onto the terminal, attempting to gain persistence." + PROPAGATE = 4 + "The malware attempts to spread to other systems or networks, looking for vulnerable services." + COMMAND_AND_CONTROL = 5 + "The malware establishes a connection to an external command and control server." + PAYLOAD = 6 + "The malware performs its intended malicious activities, dependent on payload." + + # These Enums must be included in all kill chains. + # Due to limitations in Python and Enums, it is not possible to inherit these Enums from an base class. + + NOT_STARTED = 100 + "Indicates that the Kill Chain has not started." + SUCCEEDED = 200 + "Indicates that the kill chain has succeeded." + FAILED = 300 + "Indicates that the attack has failed." + + def initial_stage(self) -> "MobileMalwareKillChain": + """Returns the first stage in the kill chain. Used by Abstract TAP for TAP Agent Setup.""" + return self.DOWNLOAD + + +class PortStatus(Enum): + """ + Enumeration which represent TAP001's "Knowledge" of a port in the simulation. + + Used to by the Propagate kill chain stage to track the current understanding of the + simulation by the TAP001 agent. + + Additionally, also used to define failure and success clauses. + """ + + UNKNOWN = 0 + "Indicates that the status of the port is unknown." + OPEN = 1 + "Indicates that the port is open in the simulation." + CLOSED = 2 + "Indicates that the port is closed in the simulation." + + +class TAP001(AbstractTAP, discriminator="tap-001"): + """ + TAP001 | Mobile Malware -- Ransomware Variant. + + Currently, implements the ransomware variant Mobile Malware kill chain. + + This Threat Actor Profile (TAP) represents ransomware. + After gaining access to a host, the ransomware scans the + network to find the target database and then corrupts it. + + At current, TAP001's final stage (corruption) is limited to + database servers. + + Please see the TAP001-Kill-Chain-E2E.ipynb for more information. + """ + + class AgentSettingsSchema(AbstractTAP.AgentSettingsSchema): + """TAP001's AgentSettings schema (Expands upon the inherited AbstractTAP `AgentSettingsSchema`).""" + + target_ips: Optional[List[StrIP]] = [] + default_target_ip: StrIP + kill_chain: MobileMalwareKillChainOptions # = Field(default_factory=lambda: MobileMalwareKillChainOptions()) + + class ConfigSchema(AbstractTAP.ConfigSchema): + """Config Schema for the TAP001 agent.""" + + type: Literal["tap-001"] = "tap-001" + agent_settings: "TAP001.AgentSettingsSchema" = Field(default_factory=lambda: TAP001.AgentSettingsSchema()) + + config: ConfigSchema # = Field(default_factory=lambda: TAP001.ConfigSchema()) + + selected_kill_chain: Type[MobileMalwareKillChain] = MobileMalwareKillChain + + last_scan_timestep: list[int] = [] + "Timesteps when the agent has performed scans. Used for reading agent action history" + last_scan_type: Optional[Literal["Ping", "Port"]] = None + "The type of scan that was previously performed." + scans_complete: int = 0 + "Number of scans completed" + networks_scanned: int = 0 + "Number of subnets that have been scanned" + permitted_attempts: int = 10 + "The permitted amount of scans allowed before the scan is considered to have failed" + c2_settings: dict = {} + "Dictionary containing all C2 stage relevant user settings." + payload_settings: dict = {} + "Dictionary containing all Payload stage relevant internal and user settings." + chosen_application: str = "" + """The name of the agent's currently chosen application.""" + target_ip: Optional[IPV4Address] = None + """TAP001's current target ip. This attribute is changed dynamically through out the kill chain.""" + + network_knowledge: Dict = {} + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.setup_agent() + + def _progress_kill_chain(self) -> None: + """Private Method used to progress the kill chain to the next stage.""" + if self.next_kill_chain_stage == MobileMalwareKillChain.PAYLOAD: # Covering final stage edge case. + self.current_kill_chain_stage = self.selected_kill_chain(self.current_kill_chain_stage + 1) + self.next_kill_chain_stage = self.selected_kill_chain.SUCCEEDED + else: + # Otherwise, set the current stage to the previous next and increment the next kill chain stage. + self.current_kill_chain_stage = self.next_kill_chain_stage + + if self.current_kill_chain_stage == self.selected_kill_chain.SUCCEEDED: + self.next_kill_chain_stage = self.selected_kill_chain.NOT_STARTED + else: + self.next_kill_chain_stage = self.selected_kill_chain(self.current_kill_chain_stage + 1) + + self.current_stage_progress = KillChainStageProgress.PENDING + + def setup_agent(self) -> None: + """Responsible for agent setup upon episode reset. + + Explicitly this method performs the following: + 1. Loads the inherited attribute 'selected_kill_chain' with the MobileMalwareKillChain + 2. Selects the starting node frm the given user TAP settings. + 3. Selects the target node frm the given user TAP settings. + 4. Sets the next execution timestep to the given user tap settings - The starting step. + 5. Sets the TAP's current host as the selected starting node. + 6. instantiates the required class attributes for performing the PROPAGATE step. + """ + # TAP Boilerplate Setup + self._setup_agent_kill_chain(MobileMalwareKillChain) + + # TAP001 Specific Setup + self._select_start_node() + self._select_target_ip() + self._set_next_execution_timestep(self.config.agent_settings.start_step) + self.current_host = self.starting_node + + # The permitted amount of scans allowed before the scan is considered to have failed. + self.permitted_attempts = self.config.agent_settings.kill_chain.PROPAGATE.scan_attempts + + # The current simulation knowledge. + self.network_knowledge: Dict = { + "target_found": False, # Used for the propagate step + # Using Enums rather than close open Set + # _scan_failure_handler clauses will assume that a target does not have a open port an action too early + # If we use "target_port" = False as the default value. + # As we need to perform to a Port Scan Before a target_port can be confirmed as either open or closed. + "target_port": PortStatus.UNKNOWN, + "target_ip": self.target_ip, + "next_scan_target": self.config.agent_settings.kill_chain.PROPAGATE.network_addresses[0], # Local Subnet + "live_hosts": {}, # Previous scan action results. + } + + # User supplied and internal C2 settings + self.c2_settings: Dict = { + "c2_server": self.config.agent_settings.kill_chain.COMMAND_AND_CONTROL.c2_server_name, + "c2_server_ip_address": self.config.agent_settings.kill_chain.COMMAND_AND_CONTROL.c2_server_ip, + "keep_alive_frequency": self.config.agent_settings.kill_chain.COMMAND_AND_CONTROL.keep_alive_frequency, + "masquerade_protocol": self.config.agent_settings.kill_chain.COMMAND_AND_CONTROL.masquerade_protocol, + "masquerade_port": self.config.agent_settings.kill_chain.COMMAND_AND_CONTROL.masquerade_port, + "beacon_configured": False, + } + # User supplied and internal Payload Stage Settings + self.payload_settings: Dict = { + "target_file_name": "database.db", + "target_folder_name": "database", + "exfiltration_folder_name": self.config.agent_settings.kill_chain.PAYLOAD.exfiltration_folder_name, + "target_ip_address": self.target_ip, + "target_username": self.config.agent_settings.kill_chain.PAYLOAD.target_username, + "target_password": self.config.agent_settings.kill_chain.PAYLOAD.target_password, + "corrupt": self.config.agent_settings.kill_chain.PAYLOAD.corrupt, + "exfiltrate": self.config.agent_settings.kill_chain.PAYLOAD.exfiltrate, + "continue_on_failed_exfil": self.config.agent_settings.kill_chain.PAYLOAD.continue_on_failed_exfil, + } + + def get_action(self, obs: ObsType, timestep: int) -> Tuple[str, Dict]: + """Follows the TAP001's Mobile Malware Kill Chain. + + Calls the next TAP001 Action Stage. Uses private methods to schedule kill chain stages. + See TAP001-Kill-Chain-E2E.ipynb for further information on the TAP001 agent. + + :param obs: Current observation for this agent. + :type obs: ObsType + :param timestep: The current simulation timestep, used for scheduling actions + :type timestep: int + :return: Action formatted in CAOS format + :rtype: Tuple[str, Dict] + """ + if timestep < self.next_execution_timestep or self.actions_concluded: + return "do-nothing", {} # bypasses self.chosen_action + + # self.current_timestep is currently the previous execution timestep + # So it can be used to index action history. + if not self._tap_return_handler(self.current_timestep): + # Propagate kill chain stage handles simulation failure independently. + # See _scan_setup_handler for more information. + if self.current_kill_chain_stage == MobileMalwareKillChain.PROPAGATE: + pass + elif ( + (self.current_kill_chain_stage == MobileMalwareKillChain.PAYLOAD) + and (self.current_stage_progress == KillChainStageProgress.IN_PROGRESS) + and (self.payload_settings["continue_on_failed_exfil"]) + ): + pass + else: + # Repeating the previously chosen action if the last action was unsuccessful. + self._set_next_execution_timestep(timestep + self.config.agent_settings.frequency) + self._tap_outcome_handler(MobileMalwareKillChain) + self.update_current_timestep(new_timestep=timestep) + self._set_next_execution_timestep(timestep + self.config.agent_settings.frequency) + return self.chosen_action + + self.update_current_timestep(new_timestep=timestep) + self._set_next_execution_timestep( + timestep + self.config.agent_settings.frequency + ) # Sets the next execution step. + + self._tap_outcome_handler(MobileMalwareKillChain) # Handles successes and failures + + # The kill chain is called in reverse order + # The kill chain sequence must be called in reverse order to ensure proper execution. + # Otherwise the guard clauses within these methods that check for the correct kill chain stage + # Will no longer function as intended and lead to multiple methods overwriting self.chosen_action + + self._payload() # Final kill chain stage + self._c2c() + self._propagate() + self._activate() + self._install() + self._download() + self._tap_start(MobileMalwareKillChain) # First kill chain stage + + return self.chosen_action + + def _download(self) -> None: + """Mobile Malware | Download Stage. + + First stage in the Mobile Malware Kill Chain. + Requires Two CAOS actions before progressing to the next kill chain stage. + + Performs the each action in the following order: + - Creates a folder called "downloads" via node-folder-create + - Creates a "malware_dropper.ps1" file via node-folder-create + + OBS Impact: + STARTING_HOST:FOLDERS:FOLDER:* + STARTING_HOST:FOLDERS:FOLDER:FILES:FILE:* + STARTING_HOST:num_file_creations + STARTING_HOST:num_file_deletions + """ + if self.current_kill_chain_stage == self.selected_kill_chain.DOWNLOAD: # No Probability on Download + # Execution flow _download kill chain stage + if self.current_stage_progress == KillChainStageProgress.PENDING: + self.current_host = self.starting_node + self.logger.info(f"TAP001 reached the {self.current_kill_chain_stage.name} stage") + self.logger.info( + f"The malware dropper is attempting to download onto {self.current_host}'s downloads folder!" + ) + self.chosen_action = "node-folder-create", { + "node_name": self.current_host, + "folder_name": "downloads", + } + self.current_stage_progress = KillChainStageProgress.IN_PROGRESS + # Execution flow for the second execution + elif self.current_stage_progress == KillChainStageProgress.IN_PROGRESS: + self.logger.info(f"The malware has entered {self.current_host}'s downloads folder!") + self.chosen_action = "node-file-create", { + "node_name": self.current_host, + "folder_name": "downloads", + "file_name": "malware_dropper.ps1", + "force": True, + } + self.current_stage_progress = KillChainStageProgress.FINISHED + # Progresses the kill chain after the second execution. + if self.current_stage_progress == KillChainStageProgress.FINISHED: + self._progress_kill_chain() + + def _install(self) -> None: + """Mobile Malware | Install Stage. + + Second stage in the Mobile Malware Kill Chain. + Accesses the previously created file "malware_dropper.ps1" via node-file-access + + Observation Space Impact(s): + + STARTING_HOST:FOLDERS:FOLDER:FILES:FILE:NUM_FILE_ACCESS + """ + if self.current_kill_chain_stage == self.selected_kill_chain.INSTALL: # No Probability on Install + self.logger.info(f"TAP001 reached the {self.current_kill_chain_stage.name} stage.") + self.logger.info(f"The malware is attempting to gain persistence on {self.target_ip}.") + self.current_host = self.starting_node + self.chosen_action = "node-file-access", { + "node_name": self.current_host, + "folder_name": "downloads", + "file_name": "malware_dropper.ps1", + } + # This stage only takes one action so there is no need for self.current_stage_progress + self._progress_kill_chain() + + def _activate(self) -> None: + """Mobile Malware | Activate Stage. + + First stage in the Mobile Malware Kill Chain. + Requires One CAOS action before progressing to the next kill chain stage. + + Installs RansomwareScript via node-application-install + + Observation Space Impact(s): + + STARTING_HOST:APPLICATIONS:APPLICATION:* + """ + if self.current_kill_chain_stage == self.selected_kill_chain.ACTIVATE: # No Probability on Activate + # Upon stage entry perform the following: + self.logger.info(f"TAP001 {self.config.ref} has reached the {self.current_kill_chain_stage.name} stage") + # Select the starting host + self.current_host = self.starting_node + + # Selecting the ransomware-script as the next application to install + self.chosen_application = "ransomware-script" + + self.current_stage_progress = KillChainStageProgress.FINISHED + + self.logger.info(f"Attempting to install {self.chosen_application} on {self.starting_node}") + + self.chosen_action = "node-application-install", { + "node_name": self.current_host, + "application_name": self.chosen_application, + } + self._progress_kill_chain() + + def _propagate(self) -> None: + """Mobile Malware | Propagate Stage. + + Fourth stage in the Insider kill chain. + Performs a trial using the given user PROPAGATE stage probability. + + This stage requires dynamic interaction within the simulation + and thus the amount of steps required to progress the stage + is dependent on the TAP's chosen target and starting node. + + Port Scan Action(s): + node-nmap-port-scan + node-nmap-ping-scan + node-network-service-recon + + Observation Space Impact(s): + + *:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* + STARTING_HOST:APPLICATIONS:APPLICATION:OPERATING_STATUS:operating_status + """ + if self.current_kill_chain_stage == self.selected_kill_chain.PROPAGATE: + # If the _propagate stage is already in progress: Continue. + if self.current_stage_progress == KillChainStageProgress.IN_PROGRESS: + self.current_stage_progress = self._scan_handler() + if self.current_stage_progress == KillChainStageProgress.FINISHED: + self._progress_kill_chain() + # Otherwise: Trial propagate stage probability and start the scan. + else: + if self._agent_trial_handler(self.config.agent_settings.kill_chain.PROPAGATE.probability): + if self.current_stage_progress == KillChainStageProgress.PENDING: + self.logger.info(f"TAP001 reached the {self.current_kill_chain_stage.name} stage") + self.current_host = self.starting_node + # Resets Propagate relevant attributes + # Prevents repeating kill chains from starting propagate with extra knowledge. + self._propagate_reset() + self.logger.info(f"Attempting to scan the network in order to locate {self.target_ip}!") + + self.chosen_action = "node-nmap-ping-scan", { + "source_node": self.current_host, + "target_ip_address": self.network_knowledge.get("next_scan_target"), + "show": False, + } + + self.scans_complete = 1 # Setting the scan count to one. + self.last_scan_timestep.append(self.current_timestep) # Appending the last_scan_timestep + self.last_scan_type = "Ping" # Setting the last_scan_type to Ping + self.current_stage_progress = KillChainStageProgress.IN_PROGRESS + else: + if self.config.agent_settings.repeat_kill_chain_stages == False: + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + self.chosen_action = "do-nothing", {} + + def _c2c(self) -> None: + """Mobile Malware | Command and Control Stage. + + Fourth stage in the Insider kill chain. + The current implementation installs, configures and setups the C2 Suite via + the following CAOS actions which are this method calls in the order below: + + 1. Installs the C2 Beacon on the starting node. ``node-application-install`` + 2. Configures the C2 Beacon to the C2 Server using given TAP settings. ``configure-c2-beacon`` + 3. Establishes connection. ``node-application-execute```. + + For a in-depth explanation of the C2 Suite please refer to the ``C2-E2E-notebook``. + + Observation Space Impact(s): + STARTING_HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* + STARTING_HOST:APPLICATIONS:APPLICATION:OPERATING_STATUS:operating_status + """ + if self.current_kill_chain_stage == self.selected_kill_chain.COMMAND_AND_CONTROL: + self.chosen_application = "c2-beacon" + # Execution flow for the Command and Control stage: + if self.current_stage_progress == KillChainStageProgress.PENDING: + # Performing a probability check on the first C2 action. Similar to Propagate. This is performed once. + if self._agent_trial_handler(self.config.agent_settings.kill_chain.COMMAND_AND_CONTROL.probability): + self.logger.info(f"TAP001 reached the {self.current_kill_chain_stage.name} stage.") + self.logger.info("Attempting to install C2 Beacon on starting host.") + self.chosen_action = "node-application-install", { + "node_name": self.current_host, + "application_name": self.chosen_application, + } + self.current_stage_progress = KillChainStageProgress.IN_PROGRESS + self.c2_settings.update({"beacon_installed": False}) # For repeat kill chains. + else: + self.chosen_action = "do-nothing", {} + if not self.config.agent_settings.repeat_kill_chain_stages: + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + + elif self.current_stage_progress == KillChainStageProgress.IN_PROGRESS: + if self.c2_settings.get("beacon_configured") == False: + self.logger.info("Attempting to configure C2 Beacon.") + + config = { + "c2_server_ip_address": self.c2_settings.get("c2_server_ip_address"), + "keep_alive_frequency": self.c2_settings.get("keep_alive_frequency"), + "masquerade_port": self.c2_settings.get("masquerade_port"), + "masquerade_protocol": self.c2_settings.get("masquerade_protocol"), + } + + self.chosen_action = "configure-c2-beacon", {"node_name": self.current_host, **config} + # Triggers the below else statement upon re-entering this method + self.c2_settings.update({"beacon_configured": True}) + else: + self.logger.info("Attempting to connect C2 Beacon to C2 Server.") + self.chosen_action = "node-application-execute", { + "node_name": self.current_host, + "application_name": self.chosen_application, + } + + self._progress_kill_chain() + + def _payload(self) -> None: + """Mobile Malware | Payload Stage. + + Fifth and final stage in the Mobile Malware Kill Chain. + Requires a single CAOS action before progressing to the next kill chain stage. + + Performs c2-server-ransomware-launch CAOS Action. + This causes the ransomware to send a malicious payload to the target + database to which causes database.db file to enter into a corrupted state. + + Observation Space Impact(s): + + TARGET_HOST:FOLDERS:FOLDER:FILES:FILE:* + STARTING_HOST:APPLICATIONS:APPLICATION:OPERATING_STATUS + """ + if self.current_kill_chain_stage == self.selected_kill_chain.PAYLOAD: + if self.current_stage_progress == KillChainStageProgress.IN_PROGRESS: + self.current_stage_progress = self._payload_handler() + + # Performing a probability trial on payload. + if self.current_stage_progress == KillChainStageProgress.PENDING: + if self._agent_trial_handler(self.config.agent_settings.kill_chain.PAYLOAD.probability): + self.logger.info(f"TAP001 reached the {self.current_kill_chain_stage.name} stage.") + self.current_host = self.c2_settings["c2_server"] + self.chosen_action = "c2-server-ransomware-configure", { + "node_name": self.current_host, + "server_ip_address": self.target_ip, + "payload": "ENCRYPT", + } + self.current_stage_progress = KillChainStageProgress.IN_PROGRESS + else: + self.chosen_action = "do-nothing", {} + if not self.config.agent_settings.repeat_kill_chain_stages: + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + + if self.current_stage_progress == KillChainStageProgress.FINISHED: + self._progress_kill_chain() + + def _payload_handler(self) -> KillChainStageProgress: + """Private method which handles the payload kill chain stage. + + This method is responsible for setting the chosen_action attribute + to either encrypt, exfiltrate the target's database.db file dependent + on user given settings. + + Returns ``KillChainStageProgress.FINISHED`` if all actions have been completed + or ``KillChainStageProgress.IN_PROGRESS`` if further actions are required + to complete this stage. + + :rtype: KillChainStageProgress + """ + if self.payload_settings["exfiltrate"] == True: + self.chosen_action = "c2-server-data-exfiltrate", { + "node_name": self.current_host, + "target_file_name": self.payload_settings.get("target_file_name"), + "target_folder_name": self.payload_settings.get("target_folder_name"), + "exfiltration_folder_name": self.payload_settings.get("exfiltration_folder_name"), + "target_ip_address": self.payload_settings.get("target_ip_address"), + "username": self.payload_settings.get("target_username"), + "password": self.payload_settings.get("target_password"), + } + self.payload_settings.update({"exfiltrate": False}) + + if self.payload_settings["corrupt"]: + return KillChainStageProgress.IN_PROGRESS + + elif self.payload_settings["corrupt"]: + self.chosen_action = "c2-server-ransomware-launch", {"node_name": self.current_host} + self.payload_settings.update({"corrupt": False}) + + return KillChainStageProgress.FINISHED + + def _scan_handler(self) -> KillChainStageProgress: + """Private method which handles the propagate kill chain stage. + + This method handles the agent action response + + Returns the current KillChainStageProgress of the scan. + If this method returns .IN_PROGRESS then the method is called on the next timestep. + + If this method returns .FINISHED then the kill chain stage is progressed. + Refer to the _propagate method for further information regarding execution flow. + + :rtype: KillChainStageProgress. + """ + # Retrieving the previous scan action's results. + previous_scan_response = self._scan_setup_handler() + + # If the previous scan was unsuccessful we need to repeat the previous scan. + # So we can skip the scan action response handler + if previous_scan_response.status == "success": + # Setting scan_results as the previous scan action's .data attribute + scan_results = previous_scan_response.data + + # Updating self.network_knowledge with the previous scan. + self._scan_action_response_handler(scan_results) + + # Before continuing, checking for edge case errors. + if self._scan_failure_handler(): + # self.chosen_action would be the previous scan at this point + # Setting self.chosen action to do nothing in order to prevent TAP001 from performing + # from performing another despite having failed the kill chain. + self.chosen_action = "do-nothing", {} + self.logger.info(f"Thus TAP001 {self.config.ref} has failed the {self.current_kill_chain_stage.name}") + if self.config.agent_settings.repeat_kill_chain_stages == False: + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + else: + self.logger.info(f"TAP001 has opted to reattempt the {self.current_kill_chain_stage.name} stage!") + # breaking out of _scan_handler early & resetting the PROPAGATE stage. + return KillChainStageProgress.PENDING + + # Setting the Next Scan Type. + next_scan_type = self._scan_logic_handler() + self._scan_action_handler(next_scan_type) + + # Final Clause - Returns either IN_PROGRESS or FINISHED. + return self._scan_progress_handler() + + def _scan_logic_handler(self) -> str: + """TAP001's private method responsible for controlling execution flow within the _scan_handler() method. + + This method is decides the next scan action to be taken by the TAP Agent dependent on the self.network_knowledge + attribute. The self.network_knowledge attribute is updated dynamically via _scan_action_response_handler() + + This method is carries out the following logic: + + 1. If the target is found; Port scan the target host. + + This to confirm that a valid target is present on the target host + before moving onto the next stage. + + 2. If the current network has not yet been Recon Scanned; Recon Scan all live hosts. + + If the current network has live hosts yet no protocol/port information has been identified, + then we need to perform a recon scan. + + 3. If the current network has been Recon Scanned; Ping Scan the next network address. + + If the current network has no currently identified live_hosts, then we must ping scan + the provided network address in order to locate some hosts to recon-scan. + + Additionally, if we have reached this clause without being caught by a previous statement then we can + move onto the next network address. + + Otherwise, If you reach the final else clause then a logic failure + has occurred somewhere before reaching this function. + + This logic is repeated until a valid target is found. + + rtype: str + """ + # If the target is found, then we only need to Port Scan the network to reach an exit condition. + # Intuitively doesn't make sense to perform another scan after the target has already been scanned, + # However, In the real world, you would perform an more aggressive scan (Port in this case) + # To identify other potential vulnerabilities on the target. + if self.network_knowledge.get("target_found") == True: + return "Port" + + # This assumes that NMAP's action response stays consistent. + if self.last_scan_type == "Ping": + # If we know that their are no suitable targets in the last_results + # then we scan skip Recon and Return Ping instead. + if self.network_knowledge.get("live_hosts") == []: + self.logger.info( + "As we didn't find any suitable hosts from the last scan - we have no need to perform a recon_scan." + ) + return "Ping" + else: + return "Recon" + + # Then the next network address needs to be scanned. + elif self.last_scan_type == "Recon": + return "Ping" + + # If we haven't caught a condition at this point then we're running into unexpected behaviour. + # Returning anything other than a ping type catches an else statement in _scan_action_handler. + else: + self.current_kill_chain_stage = MobileMalwareKillChain.FAILED + return "logic_handler_error" + + def _scan_progress_handler(self) -> KillChainStageProgress: + """TAP001 Private method used for handling success & continue cases. + + The target must be found via the simulation and a + valid database service must be installed and running on the target. + Otherwise, the scan will continue. + + :rtype: KillChainStageProgress. + """ + # Success Criteria: The Target is found and the target port is also found (Database Service at current) + if self.network_knowledge["target_found"]: + target_port = self.network_knowledge["target_port"] + if target_port == PortStatus.OPEN: + self.logger.info(f"TAP001 {self.config.ref} located the target and confirmed a valid database service!") + self.chosen_action = "do-nothing", {} # No need to perform another scan after this. + return KillChainStageProgress.FINISHED + else: + self.logger.info( + f"TAP001 {self.config.ref} located the target but not yet confirmed a valid database service." + ) + # No need to update scans_complete any further as we've located the target. + return KillChainStageProgress.IN_PROGRESS + else: + self.logger.info(f"TAP001 {self.config.ref} has not yet located the target. Scan continuing!") + # Updating the amount of scans completed. + self.scans_complete += 1 + return KillChainStageProgress.IN_PROGRESS + + def _scan_failure_handler(self) -> bool: + """TAP001 Private Method used for handling the _propagate failure edge cases. + + Returns a True if the scan entered into a failing edge case. + Otherwise returns False if no errors were encountered. + + :rtype: bool. + """ + # Simple boolean for keeping track if an edge case is encountered. + error_found = False + + # Edge Case: If the scan action finds the target but the target doesn't have a database running. + if self.network_knowledge.get("target_found"): + target_port = self.network_knowledge["target_port"] + if ( + target_port == PortStatus.CLOSED + ): # Only set to True if the target doesn't have a running database service. + self.logger.info( + f"TAP001 {self.config.ref} was able to locate {self.target_ip} - " + f"However, {self.target_ip} does not have a database service running." + ) + error_found = True + + # Edge Case: If the scan action has executed X times without finding the target. + # This edge case should be last incase of the elusive double edge-case. + # (Which would be caught first by this clause which would prevent valuable debugging logs) + if self.scans_complete >= self.permitted_attempts: + self.logger.info( + f"{self.config.ref} was unable to find {self.target_ip}" + f"within the permitted {self.permitted_attempts} scan attempts." + ) + error_found = True + + # Returning the boolean. + return error_found + + def _scan_setup_handler(self) -> RequestResponse: + """TAP001 Private method responsible for setting up the scan_return_handler on each call. + + Uses the self.last_scan_timestep list to index the self.history inherited attribute. + + Returns the results of the previous scan as an RequestResponse class object. + + :rtype RequestResponse. + """ + # Using the last_timestep variable as an Index for the last scan action. + # (This only pops the last_scan_timestep not the self.history attribute itself.) + previous_scan_action = self.history[self.last_scan_timestep.pop()] + + # Appending the current timestep to the last_scan_timestep. + self.last_scan_timestep.append(self.current_timestep) + + # Testing for do-nothing,{}'s present in the previous_action. + if previous_scan_action.action == "do-nothing": + self.logger.error("do-nothing Caught whilst in scan_handler.") + self.current_kill_chain_stage = MobileMalwareKillChain.FAILED + + # Abstract TAP handles these errors if the user set the repeat_kill_chain_stages option to false. + if previous_scan_action.response.status != "success": + self.logger.info( + f"The previous scan wasn't successful: {previous_scan_action.response.data} " + f"Re-attempting the previous scan!" + ) + + # Returning the request.response attribute in AgentActionHistory + return previous_scan_action.response + + def _scan_action_response_handler(self, scan_results: Union[Dict, list]) -> None: + """TAP001 Private method which handles any success criteria and sets the self.network_knowledge accordingly. + + This method is responsible for setting the self.network_knowledge attribute dependent on the + outcome of the previous scans in order to keep track of the current progress of the scan action. + + It's important to note that in the context of the narrative, the malware does not actually know the + ip address of the target_ip. + + The IP address of the target_ip should only ever be used to confirm that a valid-target is the target_ip + This way, users are able to configure a network with multiple databases, yet select specific target one. + + :param scan_results: The .data dictionary from the previous scan actions RequestResponse '.data' attribute. + :type scan_results: dict. + """ + # This should only ever get called after the target is already found + # Checks to see if the target has an active database_service running + # (Thus vulnerable to attack) + # At current, both there is no way to differentiate between database clients & database services. + # Thus we have to assume that the given target is a database service. + if self.network_knowledge.get("target_found"): + # TODO: Sometimes scan results doesn't contain data for target ip, this is unexpected, hence we added {} as + # default return to prevent crash. But we ought to figure out why this occurs. + for protocol, ports in scan_results.get(self.network_knowledge.get("target_ip"), {}).items(): + if protocol == "tcp": + for port in ports: + if port == PORT_LOOKUP["POSTGRES_SERVER"]: + self.logger.info(f"Found a valid target on {self.target_ip}!") + self.network_knowledge["target_port"] = PortStatus.OPEN + return # Exit out of the method, no further scans are required. + + # If we couldn't find a valid port, set the target_port to closed + # Return early. + self.network_knowledge["target_port"] = PortStatus.CLOSED + return + + # If we're reached this then we know the target wasn't found within the previous scan_results. + # Thus we can ignore the port/protocol results and convert scan_results into a list. + + # Covers both PORT and PING scans (Ping returns a List, Port returns a Dict) + if isinstance(scan_results.get("live_hosts"), list): + scan_results = scan_results.get("live_hosts") + else: + # Recon doesn't return "live_hosts" but a dict of hosts. + scan_results_list = [] + for hosts in scan_results: + scan_results_list.append(hosts) + scan_results = scan_results_list + + # Edge Case: No Returned Results. + if scan_results == []: + self.logger.info(f"Didn't find any suitable hosts from the last {self.last_scan_type} scan ") + else: # Else - Search through the host_list. + self.logger.info(f"Found {len(scan_results)} hosts in the previous scan: {scan_results}") + for host_ip in scan_results: + # Is the host the target? + if host_ip == self.network_knowledge.get("target_ip"): + # If the previous scan found the target then we don't need to scan another network + # We only need to port_scan the previous network address again. + # (Handled by _scan_progress_handler) + self.logger.info(f"Found {self.target_ip} the in previous scan!") + self.network_knowledge.update(target_found=True) + # We can return early without updating the lists as PORT scan is hard coded to use the target_ip. + return + else: + pass + + # Updating live_hosts with live_hosts list. + self.network_knowledge.update(live_hosts=scan_results) + + # Updating the next_scan target (Either a list of hosts, or the next network address.) + self._update_next_scan_target(scan_results) + + return + + # Perhaps could pass **kwargs and simplify the if ladder + def _scan_action_handler(self, scan_type: str) -> None: + """TAP001 Private method for handling the self._chosen_action attribute. + + The primary responsibility of this method is to set the self.chosen_action + to one of the following different scan agent actions dependent on the + given argument: + + Ping: node-nmap-ping-scan + Port: node-nmap-port-scan + Recon: node-network-service-recon + + :param scan_type: The .data dictionary from the previous Scan Actions RequestResponse '.data' attribute. + :type scan_type: str. + """ + if scan_type == "Ping": + self.chosen_action = "node-nmap-ping-scan", { + "source_node": self.current_host, + "target_ip_address": self.network_knowledge.get("next_scan_target"), + "show": False, + } + elif scan_type == "Port": + # As we only need to port scan the target, we can use the given target_ip. + self.network_knowledge["next_scan_target"] = self.network_knowledge.get("target_ip") + + self.chosen_action = "node-nmap-port-scan", { + "source_node": self.current_host, + "target_ip_address": self.network_knowledge.get("target_ip"), + "show": False, + } + elif scan_type == "Recon": + self.chosen_action = "node-network-service-recon", { + "source_node": self.current_host, + "target_ip_address": self.network_knowledge.get("next_scan_target"), + # Currently both database clients & servers run on POSTGRES_SERVER. + "target_port": PORT_LOOKUP["POSTGRES_SERVER"], # 5432 + "target_protocol": PROTOCOL_LOOKUP["TCP"], + "show": False, + } + else: + self.logger.error( + f"{self.config.ref}'s _scan_action_handler method encountered a unknown scan_type: {scan_type}" + ) + self.current_kill_chain_stage = MobileMalwareKillChain.FAILED + self.chosen_action = "do-nothing", {} + + # Updating the last_scan_type attribute. + self.logger.info(f"TAP001 {self.config.ref} has opted for a {scan_type} scan!") + self.last_scan_type = scan_type + return + + def _update_next_scan_target(self, scan_target: Union[list, str]) -> None: + """Updates network_knowledge.next_scan_type with the given argument dependent on the last_scan_type. + + If the last scan to be executed was a recon scan; then the next scan is updated + to be the next user given network address. (We move onto another network) + + otherwise, the live_hosts of the previous scan is selected. + + :param scan_target: The network address (str) or list of hosts (list) + to be used to update self.network_knowledge.next_scan_target + :type scan_target: str or list + """ + if self.last_scan_type == "Recon" or scan_target == []: # Covers None Edge cases. + # Edge Case: If the tap agent has ran out of networks to scan! + try: + # Tries to update the next_scan_target with the next network address + self.networks_scanned += 1 + # Now we need to move onto the next network address. + self.network_knowledge.update( + next_scan_target=self.config.agent_settings.kill_chain.PROPAGATE.network_addresses[ + self.networks_scanned + ] + ) + + except IndexError: + # Just In Case the last subnet has a valid target + # (We won't be scanning another network, so the index error is irrelevant) + if self.network_knowledge.get("target_found") == True: + pass + # Repeats from the start if repeat scan is enabled. + elif self.config.agent_settings.kill_chain.PROPAGATE.repeat_scan == True: + self.networks_scanned = 0 + self.logger.info(f"TAP001 {self.config.ref} couldn't find the target!") + self.logger.info(f"TAP001 {self.config.ref} will continue to scan the network!") + random_network_address = random.randint( + 0, len(self.config.agent_settings.kill_chain.PROPAGATE.network_addresses) - 1 + ) + self.network_knowledge.update( + next_scan_target=self.config.agent_settings.kill_chain.PROPAGATE.network_addresses[ + random_network_address + ] + ) + + else: + self.logger.info( + f"TAP001 {self.config.ref} ran out of networks to scan! Unable to find a valid target." + ) + + elif self.last_scan_type == "Ping": + # Now we need to recon scan the live_hosts so we set it as the next_scan_target. + self.network_knowledge.update(next_scan_target=scan_target) + + def _show_scan(self, markdown: bool = False) -> None: + """ + Prints a table of the current network knowledge of TAP001. + + :param markdown: Flag indicating if output should be in markdown format. + """ + network_knowledge_headers = [ + "Target Found", + "Target Port", + "Target IP", + # The scan will have already executed by the time. + # This method is called. + "Previous Scan Target", + "Previous Scan Results", + ] + network_knowledge_table = PrettyTable(network_knowledge_headers) + network_knowledge_table.title = f"TAP001 {self.config.ref}'s Current Network Knowledge" + if markdown: + network_knowledge_table.set_style(MARKDOWN) + network_knowledge_table.add_row( + [ + self.network_knowledge.get("target_found"), + self.network_knowledge.get("target_port"), + self.network_knowledge.get("target_ip"), + self.network_knowledge.get("next_scan_target"), + self.network_knowledge.get("live_hosts"), + ] + ) + print(network_knowledge_table) + + propagate_status_headers = [ + "Last Scan Timestep", + "Last Scan Type", + "Scans Complete", + "Networks Scanned", + "Permitted Scan Attempts", + ] + propagate_status_table = PrettyTable(propagate_status_headers) + propagate_status_table.title = f"TAP001 {self.config.ref}'s Propagate Stage Status" + if markdown: + propagate_status_table.set_style(MARKDOWN) + propagate_status_table.add_row( + [ + self.last_scan_timestep, + self.last_scan_type, + self.scans_complete, + self.networks_scanned, + self.permitted_attempts, + ] + ) + print(propagate_status_table) + + def _select_target_ip(self) -> None: + """ + Handles setting the target node behaviour of TAP type agents. + + If the user given tap_settings provides a target_ip list then the target node + is set to a random node given in the target_ip list. + Otherwise, the starting node is set to the 'default_target_node' option. + """ + if not self.config.agent_settings.target_ips: + self.target_ip = self.config.agent_settings.default_target_ip + else: + self.target_ip = random.choice(self.config.agent_settings.target_ips) + + def _propagate_reset(self) -> None: + """Resets the propagate relevant attributes back to their original values. + + This method is called to prevent the TAP001 agent from restarting + it's second kill chain execution with pre-existing network knowledge + """ + self.last_scan_timestep.clear() + "Clears the timesteps the last_scan timesteps." + self.last_scan_type = None + "Resets the type of scan that was performed most recently" + self.scans_complete = 0 + "Resets the number of scans completed" + self.networks_scanned = 0 + "Resets the number of subnets that have been scanned" + + self._network_knowledge_reset() + + def _network_knowledge_reset(self) -> None: + """Resets self.network_knowledge back to their starting values. + + This method is called to prevent the TAP001 agent from instantly + skipping past the Propagate Stage on it's second kill chain attempt. + (The target would have already been found in it's first run execution.) + """ + self.network_knowledge = { + "target_found": False, # Used for the propagate step + "target_port": PortStatus.UNKNOWN, + "target_ip": self.target_ip, + "next_scan_target": self.config.agent_settings.kill_chain.PROPAGATE.network_addresses[0], # Local Subnet + "live_hosts": {}, # Previous scan action results. + } diff --git a/src/primaite/game/agent/scripted_agents/TAP003.py b/src/primaite/game/agent/scripted_agents/TAP003.py new file mode 100644 index 00000000..1dcebe07 --- /dev/null +++ b/src/primaite/game/agent/scripted_agents/TAP003.py @@ -0,0 +1,468 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +from enum import IntEnum +from typing import Dict, List, Literal, Optional, Tuple, Type + +from gymnasium.core import ObsType +from pydantic import Field + +from primaite.game.agent.actions.acl import RouterACLAddRuleAction +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) + + +class InsiderKillChainOptions(KillChainOptions): + """Model validation for TAP003's Kill Chain.""" + + class _PlanningOptions(KillChainStageOptions): + """Valid options for the `PLANNING` InsiderKillChain stage.""" + + starting_network_knowledge: Dict # TODO: more specific schema here? + + class _AccessOptions(KillChainStageOptions): + """Valid options for the `ACCESS` InsiderKillChain stage.""" + + pass + + class _ManipulationOptions(KillChainStageOptions): + """Valid options for the `MANIPULATION` InsiderKillChain stage.""" + + account_changes: List[Dict] = [] # TODO: More specific schema here? + + class _ExploitOptions(KillChainStageOptions): + """Valid options for the `EXPLOIT` InsiderKillChain stage.""" + + malicious_acls: List[RouterACLAddRuleAction.ConfigSchema] = [] + + PLANNING: _PlanningOptions = Field(default_factory=lambda: InsiderKillChainOptions._PlanningOptions()) + ACCESS: _AccessOptions = Field(default_factory=lambda: InsiderKillChainOptions._AccessOptions()) + MANIPULATION: _ManipulationOptions = Field(default_factory=lambda: InsiderKillChainOptions._ManipulationOptions()) + EXPLOIT: _ExploitOptions = Field(default_factory=lambda: InsiderKillChainOptions._ExploitOptions()) + + +class InsiderKillChain(IntEnum): + """ + Enumeration representing different attack stages of the vulnerability and backdoor creation kill chain. + + This kill chain is designed around the TAP003 - Malicious Insider Corporal Pearson. + Each stage represents a specific phase in the kill chain. + Please refer to the TAP003 notebook for the current version's implementation of this kill chain. + """ + + RECONNAISSANCE = 1 + "Represents TAP003 identifying sensitive systems, data and access control mechanisms" + PLANNING = 2 + "Represents TAP003 devising a plan to exploit their elevated privileges." + ACCESS = 3 + "Represents TAP003's using legitimate credentials to access the access control settings." + MANIPULATION = 4 + "Represents TAP003 altering ACLs, User & Group Attributes & other control mechanisms to grant unauthorised access" + EXPLOIT = 5 + "Represents TAP003 exploiting their insider knowledge and privilege to implement changes for sabotage." + EMBED = 6 + "Represents TAP003's additional changes to ensure continued access" + CONCEAL = 7 + "Represents TAP003's efforts in hiding their traces of malicious activities" + EXTRACT = 8 + "Represents TAP003 removing sensitive data from the organisation, either for personal gain or to inflict harm." + ERASE = 9 + "Represents TAP003 covering their tracks by removing any tools, reverting temporary changes and logging out" + + # These Enums must be included in all kill chains. + # Due to limitations in Python and Enums, it is not possible to inherit these Enums from an base class. + + NOT_STARTED = 100 + "Indicates that the Kill Chain has not started." + SUCCEEDED = 200 + "Indicates that the kill chain has succeeded." + FAILED = 300 + "Indicates that the attack has failed." + + def initial_stage(self) -> "InsiderKillChain": + """Returns the first stage in the kill chain. Used by Abstract TAP for TAP Agent Setup.""" + return self.RECONNAISSANCE + + +class TAP003(AbstractTAP, discriminator="tap-003"): + """ + TAP003 | Malicious Insider Corporal Pearson. + + Currently implements one kill chain: Backdoor & Vulnerability Creation. + This Threat Actor Profile (TAP) aims to introduce subtle cyber attack. + For example, the Backdoor & Vulnerability creation kill chain + creates DENY firewall rules which do not trigger NMNE. + Please see the TAP003-Kill-Chain-E2E.ipynb for more information. + """ + + class AgentSettingsSchema(AbstractTAP.AgentSettingsSchema): + """Agent Settings Schema that enforces TAP003's `kill_chain` config to use the InsiderKillChainOptions.""" + + kill_chain: InsiderKillChainOptions # = Field(default_factory=lambda: MobileMalwareKillChainOptions()) + + class ConfigSchema(AbstractTAP.ConfigSchema): + """Config Schema for the TAP001 agent.""" + + type: Literal["tap-003"] = "tap-003" + agent_settings: "TAP003.AgentSettingsSchema" = Field(default_factory=lambda: TAP003.AgentSettingsSchema()) + + config: ConfigSchema + selected_kill_chain: Type[InsiderKillChain] = InsiderKillChain + _current_acl: int = 0 + network_knowledge: Dict = {} # TODO: more specific typing + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._change_password_target_host: str = "" + """If we have just sent a change password request over SSH, this variable keeps track of the hostname.""" + self._ssh_target_host: str = "" + """If we have just send a SSH_LOGIN request, keeps track of the hostname to which we are attempting to SSH.""" + self._next_account_change: Optional[Dict] = None + self._num_acls = len(self.config.agent_settings.kill_chain.EXPLOIT.malicious_acls) + + self.network_knowledge: dict = {"credentials": {}, "current_session": {}} + """Keep track of current network state based on responses after sending actions. Populated during PLANNING.""" + self.setup_agent() + + def _progress_kill_chain(self) -> None: + """Private Method used to progress the kill chain to the next stage.""" + if self.next_kill_chain_stage == self.selected_kill_chain.EXPLOIT: # Covering final stage edge case. + self.current_kill_chain_stage = self.selected_kill_chain(self.current_kill_chain_stage + 1) + self.next_kill_chain_stage = self.selected_kill_chain.SUCCEEDED + else: + # Otherwise, set the current stage to the previous next and increment the next kill chain stage. + self.current_kill_chain_stage = self.next_kill_chain_stage + + if self.current_kill_chain_stage == self.selected_kill_chain.SUCCEEDED: + self.next_kill_chain_stage = self.selected_kill_chain.NOT_STARTED + else: + self.next_kill_chain_stage = self.selected_kill_chain(self.current_kill_chain_stage + 1) + + self.current_stage_progress = KillChainStageProgress.PENDING + + def setup_agent(self) -> None: + """Responsible for agent setup upon episode reset. + + Explicitly this method performs the following: + 1. Loads the inherited attribute 'selected_kill_chain' with the InsiderKillChain + 2. Selects the starting node from the given user tap settings + 3. Selects the target node from the given user tap settings + 4. Sets the next execution timestep to the given user tap settings - start step + 5. Sets TAP's current host as the selected starting node. + """ + # TAP Boilerplate Setup + self._setup_agent_kill_chain(InsiderKillChain) + + # TAP003 Specific Setup + self._select_start_node() + self._set_next_execution_timestep(self.config.agent_settings.start_step) + self.current_host = self.starting_node + + def get_action(self, obs: ObsType, timestep: int) -> Tuple[str, Dict]: + """Follows the TAP003 Backdoor Vulnerability Kill Chain. + + Calls the next TAP003 Action Stage. Uses private methods to schedule kill chain stages. + See TAP003-Kill-Chain-E2E.ipynb for further information on the TAP003 agent. + + :param obs: Current observation for this agent. + :type obs: ObsType + :param timestep: The current simulation timestep, used for scheduling actions + :type timestep: int + :return: Action formatted in CAOS format + :rtype: Tuple[str, Dict] + """ + self._handle_login_response() + self._handle_change_password_response() + if timestep < self.next_execution_timestep or self.actions_concluded: + return "do-nothing", {} # bypasses self.chosen_action + + # self.current_timestep is currently the previous execution timestep + # So it can be used to index action history. + if not self._tap_return_handler(self.current_timestep): + # If the application is already installed, don't keep retrying - this is an acceptable fail + if self.current_kill_chain_stage == InsiderKillChain.PLANNING: + last_action = self.history[self.current_timestep].action + fail_reason = self.history[self.current_timestep].response.data["reason"] + if last_action == "node-application-install" and fail_reason == "already installed": + pass + else: + self.update_current_timestep(new_timestep=timestep) + self._set_next_execution_timestep(timestep + self.config.agent_settings.frequency) + self._tap_outcome_handler(InsiderKillChain) + return self.chosen_action + + self.update_current_timestep(new_timestep=timestep) + self._set_next_execution_timestep(timestep + self.config.agent_settings.frequency) + self._tap_outcome_handler(InsiderKillChain) # Handles successes and failures + + # The kill chain is called in reverse order + # The kill chain sequence must be called in reverse order to ensure proper execution. + + self._exploit() + self._manipulation() + self._access() + self._planning() + self._reconnaissance() + self._tap_start(InsiderKillChain) + + return self.chosen_action + + def _handle_login_response(self) -> None: + """If the last request was an SSH login attempt, update the current session in network knowledge.""" + if not self.history: + return + last_hist_item = self.history[-1] + if not last_hist_item.action == "node-session-remote-login" or last_hist_item.response.status != "success": + return + + self.network_knowledge["current_session"] = { + "hostname": self._ssh_target_host, + "ip_address": last_hist_item.response.data["ip_address"], + "username": last_hist_item.response.data["username"], + } + self.logger.debug( + f"Updating network knowledge. Logged in as {last_hist_item.response.data['username']} on " + f"{self._ssh_target_host}" + ) + + def _handle_change_password_response(self) -> None: + if not self.history: + return + last_hist_item = self.history[-1] + + # when sending remote change password command, this must get populated + if not self._change_password_target_host: + return + + if ( + last_hist_item.action == "node-send-remote-command" + and last_hist_item.parameters["command"][2] == "change_password" + and last_hist_item.response.status == "success" + ): + # changing password logs us out, so our current session needs to be cleared + self.network_knowledge["current_session"] = {} + + # update internal knowledge with the new password + ip = last_hist_item.parameters["remote_ip"] + username = last_hist_item.parameters["command"][3] + password = last_hist_item.parameters["command"][5] + hostname = self._change_password_target_host + self.network_knowledge["credentials"][hostname] = { + "ip_address": ip, + "username": username, + "password": password, + } + self.logger.debug(f"Updating network knowledge. Changed {username}'s password to {password} on {hostname}.") + self._change_password_target_host = "" + # local password change + elif last_hist_item.action == "node-account-change-password" and last_hist_item.response.status == "success": + self.network_knowledge["current_session"] = {} + username = last_hist_item.request[6] + password = last_hist_item.request[8] + hostname = last_hist_item.request[2] + self.network_knowledge["credentials"][hostname] = {"username": username, "password": password} + self.logger.debug(f"Updating network knowledge. Changed {username}'s password to {password} on {hostname}.") + self._change_password_target_host = "" + + def _reconnaissance(self) -> None: + """Insider Kill Chain | Reconnaissance Stage. + + First stage in the Insider kill chain. + + Sets the self.chosen attribute to the "do-nothing" CAOS action + and then calls the self._progress_kill_chain() method. + """ + if self.current_kill_chain_stage == self.selected_kill_chain.RECONNAISSANCE: + self.chosen_action = "do-nothing", {} + self._progress_kill_chain() + + def _planning(self) -> None: + """Insider Kill Chain | Planning Stage. + + Second stage in the Insider kill chain. + Performs a trial using the given user PLANNING stage probability. + + If the trial is successful then the agent populates its knowledge base with information from the config. + + Otherwise, the stage is not progressed. Additionally, the agent's kill chain is set + to failure if the repeat_kill_chain_stages parameter is set to FALSE. + """ + if not self.current_kill_chain_stage == self.selected_kill_chain.PLANNING: + return + + if not self._agent_trial_handler(self.config.agent_settings.kill_chain.PLANNING.probability): + if self.config.agent_settings.repeat_kill_chain_stages == False: + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + self.chosen_action = "do-nothing", {} + + else: + self.network_knowledge[ + "credentials" + ] = self.config.agent_settings.kill_chain.PLANNING.starting_network_knowledge["credentials"] + self.current_host = self.starting_node + self.logger.info("Resolving starting knowledge.") + self._progress_kill_chain() + if self.current_stage_progress == KillChainStageProgress.PENDING: + self.logger.info(f"TAP003 reached the {self.current_kill_chain_stage.name}") + + def _access(self) -> None: + """Insider Kill Chain | Planning Stage. + + Third stage in the Insider kill chain. + Performs a trial using the given user ACCESS stage probability. + + This currently does nothing. + """ + if self.current_kill_chain_stage == self.selected_kill_chain.ACCESS: + if self._agent_trial_handler(self.config.agent_settings.kill_chain.ACCESS.probability): + self._progress_kill_chain() + self.chosen_action = "do-nothing", {} + else: + if self.config.agent_settings.repeat_kill_chain_stages == False: + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + self.chosen_action = "do-nothing", {} + + def _manipulation(self) -> None: + """Insider Kill Chain | Manipulation Stage. + + Fourth stage in the Insider kill chain. + Performs a trial using the given user MANIPULATION stage probability. + + If the trial is successful, the agent will change passwords for accounts that will later be used to execute + malicious commands + + Otherwise if the stage is not progressed. Additionally, the agent's kill chain is set + to failure if the repeat_kill_chain_stages parameter is set to FALSE. + """ + if self.current_kill_chain_stage == self.selected_kill_chain.MANIPULATION: + if self._agent_trial_handler(self.config.agent_settings.kill_chain.MANIPULATION.probability): + if self.current_stage_progress == KillChainStageProgress.PENDING: + self.logger.info(f"TAP003 reached the {self.current_kill_chain_stage.name}.") + self.current_stage_progress = KillChainStageProgress.IN_PROGRESS + self.current_host = self.starting_node + account_changes = self.config.agent_settings.kill_chain.MANIPULATION.account_changes + if len(account_changes) > 0 or self._next_account_change: + if not self._next_account_change: + self._next_account_change = account_changes.pop(0) + if self._next_account_change["host"] == self.current_host: + # do a local password change + self.chosen_action = "node-account-change-password", { + "node_name": self.current_host, + "username": self._next_account_change["username"], + "current_password": self.network_knowledge["credentials"][self.current_host]["password"], + "new_password": self._next_account_change["new_password"], + } + self.logger.info("Changing local password.") + self._next_account_change = account_changes.pop(0) + self._change_password_target_host = self.current_host + else: + # make sure we are logged in via ssh to remote node + hostname = self._next_account_change["host"] + if self.network_knowledge.get("current_session", {}).get("hostname") != hostname: + self._ssh_target_host = hostname + self.chosen_action = "node-session-remote-login", { + "node_name": self.starting_node, + "username": self.network_knowledge["credentials"][hostname]["username"], + "password": self.network_knowledge["credentials"][hostname]["password"], + "remote_ip": self.network_knowledge["credentials"][hostname]["ip_address"], + } + self.logger.info(f"Logging into {hostname} in order to change password.") + # once we know we are logged in, send a command to change password + else: + self.chosen_action = "node-send-remote-command", { + "node_name": self.starting_node, + "remote_ip": self.network_knowledge["credentials"][hostname]["ip_address"], + "command": [ + "service", + "user-manager", + "change_password", + self._next_account_change["username"], + self.network_knowledge["credentials"][hostname]["password"], + self._next_account_change["new_password"], + ], + } + self.logger.info(f"Changing password on remote node {hostname}") + if len(account_changes) == 0: + self.logger.info("No further account changes required.") + self._next_account_change = None + else: + self._next_account_change = account_changes.pop(0) + self._change_password_target_host = hostname + if not self._next_account_change: + self.logger.info("Manipulation complete. Progressing to exploit...") + self._progress_kill_chain() + else: + if self.config.agent_settings.repeat_kill_chain_stages == False: + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + self.chosen_action = "do-nothing", {} + + def _exploit(self) -> None: + """Insider Kill Chain | Exploit Stage. + + Fifth stage in the Insider kill chain. + Performs a trial using the given user EXPLOIT stage probability. + + If the trial is successful then self.chosen_action attribute is set to the + "node-send-remote-command" CAOS action with the "ROUTER_ACL_ADDRULE" as it's chosen command. + + The impact of the ROUTER_ACL_ADDRULE is dependant on user given parameters. At current + the default impact of this stage is to block green agent traffic. An example of TAP003's + manipulation stage in action can be found in the TAP003 notebook. + + Otherwise if the stage is not progressed. Additionally, the agent's kill chain is set + to failure if the repeat_kill_chain_stages parameter is set to FALSE. + """ + if self.current_kill_chain_stage == self.selected_kill_chain.EXPLOIT: + if self.current_kill_chain_stage == KillChainStageProgress.PENDING: + # Perform the probability of success once upon entering the stage. + if not self._agent_trial_handler(self.config.agent_settings.kill_chain.EXPLOIT.probability): + if self.config.agent_settings.repeat_kill_chain_stages == False: + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + self.chosen_action = "do-nothing", {} + return + self.current_kill_chain_stage = KillChainStageProgress.IN_PROGRESS + + self.config.agent_settings.kill_chain.EXPLOIT.malicious_acls = ( + self.config.agent_settings.kill_chain.EXPLOIT.malicious_acls + ) + self._num_acls = len(self.config.agent_settings.kill_chain.EXPLOIT.malicious_acls) + malicious_acl = self.config.agent_settings.kill_chain.EXPLOIT.malicious_acls[self._current_acl] + hostname = malicious_acl.target_router + + if self.network_knowledge.get("current_session", {}).get("hostname") != hostname: + self._ssh_target_host = hostname + self.chosen_action = "node-session-remote-login", { + "node_name": self.starting_node, + "username": self.network_knowledge["credentials"][hostname]["username"], + "password": self.network_knowledge["credentials"][hostname]["password"], + "remote_ip": self.network_knowledge["credentials"][hostname]["ip_address"], + } + self.logger.info(f"Logging into {hostname} in order to add ACL rules.") + # once we know we are logged in, send a command to change password + else: + self.chosen_action = "node-send-remote-command", { + "node_name": self.starting_node, + "remote_ip": self.network_knowledge["credentials"][hostname]["ip_address"], + "command": [ + "acl", + "add_rule", + malicious_acl.permission, + malicious_acl.protocol_name, + str(malicious_acl.src_ip), + str(malicious_acl.src_wildcard), + malicious_acl.src_port, + str(malicious_acl.dst_ip), + str(malicious_acl.dst_wildcard), + malicious_acl.dst_port, + malicious_acl.position, + ], + } + self.logger.info(f"Adding ACL rule to {hostname}") + self._current_acl = self._current_acl + 1 + + if self._current_acl == self._num_acls: + self._current_acl = 0 + self.logger.info("Finished adding ACL rules.") + self._progress_kill_chain() diff --git a/src/primaite/game/agent/scripted_agents/__init__.py b/src/primaite/game/agent/scripted_agents/__init__.py index 5a97d15b..90a99d01 100644 --- a/src/primaite/game/agent/scripted_agents/__init__.py +++ b/src/primaite/game/agent/scripted_agents/__init__.py @@ -1,6 +1,21 @@ # © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK from primaite.game.agent import interface -from primaite.game.agent.scripted_agents import abstract_tap, data_manipulation_bot, probabilistic_agent, random_agent +from primaite.game.agent.scripted_agents import ( + abstract_tap, + data_manipulation_bot, + probabilistic_agent, + random_agent, + TAP001, + TAP003, +) -__all__ = ("abstract_tap", "data_manipulation_bot", "interface", "probabilistic_agent", "random_agent") +__all__ = ( + "abstract_tap", + "data_manipulation_bot", + "interface", + "probabilistic_agent", + "random_agent", + "TAP001", + "TAP003", +) diff --git a/src/primaite/game/agent/scripted_agents/abstract_tap.py b/src/primaite/game/agent/scripted_agents/abstract_tap.py index 679f69fa..a7e2124f 100644 --- a/src/primaite/game/agent/scripted_agents/abstract_tap.py +++ b/src/primaite/game/agent/scripted_agents/abstract_tap.py @@ -1,49 +1,202 @@ # © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK -from __future__ import annotations import random -from abc import ABC, abstractmethod -from typing import Dict, List, Optional, Tuple +from abc import abstractmethod +from enum import Enum, IntEnum +from typing import Dict, List, Optional, Tuple, Type -from gymnasium.core import ObsType -from pydantic import Field +from pydantic import BaseModel, ConfigDict, Field -from primaite.game.agent.scripted_agents.random_agent import PeriodicAgent - -__all__ = "AbstractTAPAgent" +from primaite.game.agent.interface import AbstractScriptedAgent +from primaite.game.science import simulate_trial -class AbstractTAPAgent(PeriodicAgent, ABC): - """Base class for TAP agents to inherit from.""" +# This class is required for abstract tap. The IntEnums in this class are repeated in other kill chains. +class BaseKillChain(IntEnum): + """A generic kill chain for abstract tap initialisation. - config: "AbstractTAPAgent.ConfigSchema" = Field(default_factory=lambda: AbstractTAPAgent.ConfigSchema()) - next_execution_timestep: int = 0 + The IntEnums in this class are repeated in other kill chains + As IntEnums cannot be directly extended by a inheritance. + """ - class AgentSettingsSchema(PeriodicAgent.AgentSettingsSchema, ABC): - """Schema for the `agent_settings` part of the agent config.""" + NOT_STARTED = 100 + "Indicates that the Kill Chain has not started." + SUCCEEDED = 200 + "Indicates that the kill chain has succeeded." + FAILED = 300 + "Indicates that the attack has failed." - possible_starting_nodes: List[str] = Field(default_factory=list) + # The original approach is to extend the base class during runtime via class methods. + # However, this approach drastically impacted the readability and complexity of the code + # So the decision was made to ignore the DRY Principle for kill chains. - class ConfigSchema(PeriodicAgent.ConfigSchema, ABC): - """Configuration schema for Abstract TAP agents.""" + @abstractmethod + def initial_stage(self) -> "BaseKillChain": + """Returns the first stage in the kill chain. Used for Abstract TAP Setup.""" + return self.NOT_STARTED - type: str = "abstract-tap" - agent_settings: AbstractTAPAgent.AgentSettingsSchema = Field( - default_factory=lambda: AbstractTAPAgent.AgentSettingsSchema() + +class KillChainStageProgress(Enum): + """Generic Progress Enums. Used by TAP Agents to keep track of kill chain stages that required multiple actions.""" + + PENDING = 0 + """Indicates that the current kill chain stage is yet to start.""" + IN_PROGRESS = 1 + """Indicates that the current kill chain stage is not yet completed.""" + FINISHED = 2 + """Indicates that the current kill chain stage stage has been completed.""" + + +class KillChainOptions(BaseModel): + """Base Class for Kill Chain Options. Inherited by all TAP Type Agents.""" + + model_config = ConfigDict(extra="forbid") + + +class KillChainStageOptions(BaseModel): + """Shared options for generic Kill Chain Stages.""" + + model_config = ConfigDict(extra="forbid") + probability: float = 1 + + +class AbstractTAP(AbstractScriptedAgent): + """Abstract class for Threat Actor Persona (TAP) Type Agents must inherit from. + + This abstract base class provides TAP agents an interface which provides + TAP type agents the necessary methods to execute kill chain(s) with + configurable parameters. + + TAP Actions are returned to the Request Manager as a Tuple + in CAOS format via the get_action method in line with other agents. + + Abstract TAP Class intends to provide each TAP the following: + + 1. Kill Chain Progression + + Kill Chains are IntEnums which define the different stages within a kill chain. + These stages are intended to be used across multiple ARCD environments. + + 2. Abstract Methods For Kill Chain Control Flow + + Abstract methods _progress_kill_chain & _setup_kill_chain + are intended to provide TAP type agent additional control + over execution flow in comparison to AbstractScriptedAgent. + + Usually these methods handle kill chain progression & success criteria. + + For more information about Abstract TAPs please refer + to the methods & attributes documentation directly. + + Additionally, Refer to a specific TAP for a more specific example. + """ + + class AgentSettingsSchema(AbstractScriptedAgent.AgentSettingsSchema): + """Agent Settings Schema. Default settings applied for all threat actor profiles.""" + + start_step: int = 5 + frequency: int = 5 + variance: int = 0 + repeat_kill_chain: bool = False + repeat_kill_chain_stages: bool = True + starting_nodes: Optional[List[str]] = [] + default_starting_node: str + kill_chain: KillChainOptions + + class ConfigSchema(AbstractScriptedAgent.ConfigSchema): + """Configuration schema applicable to all TAP agents.""" + + agent_settings: "AbstractTAP.AgentSettingsSchema" = Field( + default_factory=lambda: AbstractTAP.AgentSettingsSchema() ) - starting_node: Optional[str] = None + config: ConfigSchema = Field(default_factory=lambda: AbstractTAP.ConfigSchema()) + + selected_kill_chain: Type[BaseKillChain] + """A combination of TAP's base & default kill chain. Loaded dynamically during agent setup.""" + next_execution_timestep: int = 0 + """The next timestep in which the agent will attempt to progress the kill chain.""" + starting_node: str = "" + """The name (string) of TAP agent's starting node. This attribute is initialised via _self_select_starting_node.""" + + actions_concluded: bool = False + """Boolean value which indicates if a TAP Agent has completed it's attack for the episode.""" + + next_kill_chain_stage: BaseKillChain = BaseKillChain.NOT_STARTED + """The IntEnum of the next kill chain stage to be executed. + + This attribute is initialised via _tap_start. + Afterwards, this attribute is loaded dynamically via _progress_kill_chain. + """ + current_kill_chain_stage: BaseKillChain = BaseKillChain.NOT_STARTED + """The TAP agent's current kill chain. + + This attribute is used as a state to indicate the current progress in a kill chain. + """ + current_stage_progress: KillChainStageProgress = KillChainStageProgress.PENDING + """The TAP agent's current progress in a stage within a kill chain. + + This attribute is used as a state to indicate the current progress in a individual kill chain stage. + + Some TAP's require multiple actions to take place before moving onto the next stage in a kill chain. + This attribute is used to keep track of the current progress within an individual stage. + """ + chosen_action: Tuple[str, Dict] = "do-nothing", {} + """The next agent's chosen action. Returned in CAOS format at the end of each timestep.""" + + current_host: str = "" + """The name (str) of a TAP agent's currently selected host. + + This attribute is set dynamically during tap execution via _set_current_host. + """ + current_timestep: int = 0 + """The current timestep (int) of the game. + + This attribute is set to the "timestep" argument passed to get_action. + Mainly used to by kill chain stages for complex execution flow that is dependant on the simulation. + + Specifically, this attribute is used for indexing previous actions in the self.history inherited attribute. + + For more information please refer to AbstractAgent's "self.history" attribute + And for action responses see 'request.py' and the .data attribute. + + Lastly, a demonstration of the above capability can be found in the PROPAGATE step in the tap001-e2e notebook. + """ + + def update_current_timestep(self, new_timestep: int): + """Updates the current time_step attribute to the given timestep argument.""" + self.current_timestep = new_timestep @abstractmethod - def get_action(self, obs: ObsType, timestep: int = 0) -> Tuple[str, Dict]: - """Return an action to be taken in the environment.""" - return super().get_action(obs=obs, timestep=timestep) + def _progress_kill_chain(self): + """Private Abstract method which defines the default kill chain progression. - @abstractmethod - def setup_agent(self) -> None: - """Set up agent.""" + This abstract method intend to allow TAPs to control the logic flow of their kill chain. + In a majority of cases this method handles the success criteria and incrementing the current kill chain intenum. + + This method is abstract so TAPs can configure this behaviour for tap specific implementations. + """ pass + def _select_start_node(self) -> None: + """ + Handles setting the starting node behaviour of TAP type agents. + + If the user given tap_settings provides a starting_node list then the starting node + is set to a random node given in the starting_node list. + Otherwise, the starting node is set to the 'default_starting_node' option. + """ + # Catches empty starting nodes. + if not self.config.agent_settings.starting_nodes: + self.starting_node = self.config.agent_settings.default_starting_node + else: + self.starting_node = random.choice(self.config.agent_settings.starting_nodes) + + def _setup_agent_kill_chain(self, given_kill_chain: BaseKillChain) -> None: + """Sets the 'next_kill_chain_stage' TAP attribute via the public kill chain method 'initial_stage'.""" + self.selected_kill_chain = given_kill_chain + self.next_kill_chain_stage = self.selected_kill_chain.initial_stage(given_kill_chain) + def _set_next_execution_timestep(self, timestep: int) -> None: """Set the next execution timestep with a configured random variance. @@ -54,8 +207,96 @@ class AbstractTAPAgent(PeriodicAgent, ABC): ) self.next_execution_timestep = timestep + random_timestep_increment - def _select_start_node(self) -> None: - """Set the starting starting node of the agent to be a random node from this agent's action manager.""" - # we are assuming that every node in the node manager has a data manipulation application at idx 0 - self.starting_node = random.choice(self.config.agent_settings.possible_starting_nodes) - self.logger.debug(f"Selected starting node: {self.starting_node}") + def _agent_trial_handler(self, agent_probability_of_success: int) -> bool: + """Acts as a wrapper around simulate trial - Sets kill chain stage to failed if the relevant setting is set. + + :param agent_probability_of_success: The probability of the action success to be passed to simulate_trial. + :type agent_probability_of_success: int. + :rtype: Bool. + """ + if simulate_trial(agent_probability_of_success): + return True + else: + self.logger.info( + f"failed to reach kill chain stage {self.next_kill_chain_stage.name} due to probability of failure." + ) + if self.config.agent_settings.repeat_kill_chain_stages == False: + self.logger.info(f"Thus {self.config.ref} has failed the kill chain") + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + return False + else: + self.logger.info(f"Retrying from stage {self.current_kill_chain_stage.name}.") + return False + + def _tap_outcome_handler(self, selected_kill_chain_class: BaseKillChain) -> None: + """ + Default TAP behaviour for base kill chain stages. + + Upon Success and failure: + TAPs will either repeat or re-attack dependant on the user given settings. + + :param tap_kill_chain: The TAP agent's currently selected kill chain. + :type tap_kill_chain: BaseKillChain + """ + if ( + self.current_kill_chain_stage == self.selected_kill_chain.SUCCEEDED + or self.current_kill_chain_stage == self.selected_kill_chain.FAILED + ): + if self.actions_concluded == True: # Prevents Further logging via a guard clause boolean + self.chosen_action = "do-nothing", {} + return + if self.current_kill_chain_stage == self.selected_kill_chain.SUCCEEDED: + self.logger.info(f"{self.config.ref} has successfully carried out the kill chain.") + if self.current_kill_chain_stage == self.selected_kill_chain.FAILED: + self.logger.info(f"{self.config.ref} has failed the Kill Chain.") + if self.config.agent_settings.repeat_kill_chain == True: + self.logger.info(f"{self.config.ref} has opted to re-attack!") + self.current_kill_chain_stage = BaseKillChain.NOT_STARTED + self.next_kill_chain_stage = selected_kill_chain_class.initial_stage(selected_kill_chain_class) + else: + self.logger.info(f"{self.config.ref} has opted to forgo any further attacks.") + self.actions_concluded = True # Guard Clause Bool + self.chosen_action = "do-nothing", {} + + def _tap_return_handler(self, timestep: int) -> bool: + # Intelligence | Use the request_response system to enable different behaviour + """ + Handles the request_manager's response query. Sets Kill Chain to false if failed. + + If the previous action failed due to the simulation state, + the kill chain is considered to have failed. + + Returns True if the previous action was successful. + Returns False if the previous action was any other state. + (Including Pending and Failure) + + :param timestep: The current primAITE game layer timestep. + :type timestep: int + :rtype bool + """ + if self.history[timestep].response.status != "success": + self.logger.info( + f"{self.config.ref} has failed to successfully carry out {self.current_kill_chain_stage.name}" + ) + self.logger.info(f"due to the simulation state: {self.history[timestep].response.data}") + if self.config.agent_settings.repeat_kill_chain_stages == False: + self.logger.info( + f"Thus {self.config.ref} has failed this kill chain attempt on {self.current_kill_chain_stage.name}" + ) + self.current_kill_chain_stage = self.selected_kill_chain.FAILED + else: + self.logger.info(f"Retrying from stage {self.current_kill_chain_stage.name}!") + return False + return True + + def _tap_start(self, tap_kill_chain: Type[BaseKillChain]) -> None: + """ + Sets the TAP Agent's beginning current/next kill chain stages. + + :param IntEnum tap_kill_chain: A currently selected kill chain. + """ + if self.current_kill_chain_stage == self.selected_kill_chain.NOT_STARTED: + self.current_kill_chain_stage = tap_kill_chain.initial_stage(tap_kill_chain) + self.next_kill_chain_stage = self.selected_kill_chain(self.current_kill_chain_stage + 1) + self.logger.info(f"{self.config.ref} has begun it's attack!") + self.chosen_action = "do-nothing", {} diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index a3b77ec3..a3a790cc 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -14,6 +14,7 @@ from primaite.simulator import SIM_OUTPUT from primaite.simulator.network.creation import NetworkNodeAdder from primaite.simulator.network.hardware.base import NetworkInterface, Node, NodeOperatingState, UserManager from primaite.simulator.network.hardware.nodes.host.host_node import NIC +from primaite.simulator.network.hardware.nodes.network.firewall import Firewall # noqa: F401 from primaite.simulator.network.hardware.nodes.network.switch import Switch from primaite.simulator.network.hardware.nodes.network.wireless_router import WirelessRouter from primaite.simulator.network.nmne import NMNEConfig diff --git a/src/primaite/notebooks/Command-and-Control-E2E-Demonstration.ipynb b/src/primaite/notebooks/Command-and-Control-E2E-Demonstration.ipynb index 7e64c3c5..55adf42a 100644 --- a/src/primaite/notebooks/Command-and-Control-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Command-and-Control-E2E-Demonstration.ipynb @@ -1041,7 +1041,7 @@ "outputs": [], "source": [ "# Attempting to install the C2 RansomwareScript\n", - "ransomware_install_command = {\"commands\":[[\"software_manager\", \"application\", \"install\", \"RansomwareScript\"]],\n", + "ransomware_install_command = {\"commands\":[[\"software_manager\", \"application\", \"install\", \"ransomware-script\"]],\n", " \"username\": \"admin\",\n", " \"password\": \"admin\"}\n", "\n", @@ -1129,7 +1129,7 @@ "outputs": [], "source": [ "# Attempting to install the C2 RansomwareScript\n", - "ransomware_install_command = {\"commands\":[\"software_manager\", \"application\", \"install\", \"RansomwareScript\"],\n", + "ransomware_install_command = {\"commands\":[\"software_manager\", \"application\", \"install\", \"ransomware-script\"],\n", " \"username\": \"admin\",\n", " \"password\": \"admin\"}\n", "\n", @@ -1254,7 +1254,7 @@ "metadata": {}, "outputs": [], "source": [ - "database_server: Server = blue_env.game.simulation.network.get_node_by_hostname(\"database_server\")\n", + "database_server: Server = blue_env.game.simulation.network.get_node_by_hostname(\"database-server\")\n", "database_server.software_manager.file_system.show(full=True)" ] }, @@ -1670,6 +1670,16 @@ "\n", "display_obs_diffs(tcp_c2_obs, udp_c2_obs, blue_config_env.game.step_counter)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "env.game.agents[\"CustomC2Agent\"].show_history()" + ] } ], "metadata": { diff --git a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb index 41bbf569..e7da2d15 100644 --- a/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb +++ b/src/primaite/notebooks/Data-Manipulation-E2E-Demonstration.ipynb @@ -700,7 +700,7 @@ ], "metadata": { "kernelspec": { - "display_name": "venv2", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -714,7 +714,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.10" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/src/primaite/notebooks/Terminal-Processing.ipynb b/src/primaite/notebooks/Terminal-Processing.ipynb index 755b0184..55de6b1f 100644 --- a/src/primaite/notebooks/Terminal-Processing.ipynb +++ b/src/primaite/notebooks/Terminal-Processing.ipynb @@ -16,6 +16,13 @@ "## Simulation Layer Implementation." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulation Layer Implementation." + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/src/primaite/notebooks/UC7-E2E-Demo.ipynb b/src/primaite/notebooks/UC7-E2E-Demo.ipynb new file mode 100644 index 00000000..a1cbdd22 --- /dev/null +++ b/src/primaite/notebooks/UC7-E2E-Demo.ipynb @@ -0,0 +1,1619 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# UC7 Demonstration\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n", + "\n", + "\n", + "UC7 is a cybersecurity scenario set in a generic medium sized organisation, where multiple LAN networks which interconnect via the 'internet' to represent a corporate WAN. Comprising of four major networks; `Home Office (HOME)`, `INTERNET (ISP)`, `REMOTE SITE (REMOTE)` and the larger main site `SOME_TECH`. Each network is comprised of routers, switches, computers and servers which green agents use to represent a more real-world accurate pattern of life.\n", + "\n", + "Intended to be a step-up from the [smaller network of UC2](./Data-Manipulation-E2E-Demonstration.ipynb), UC7 introduces two new potential attacks (TAPS) that the blue agent must defend against. \n", + "\n", + "_This notebook acts as the starting point for any users unfamiliar with UC7 and will also sign post to any other UC7 relevant notebooks for further information._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.system.services.dns.dns_server import DNSServer\n", + "from primaite.simulator.system.software import SoftwareHealthState\n", + "from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus\n", + "from primaite.simulator.network.hardware.nodes.network.switch import Switch\n", + "from primaite.simulator.system.applications.web_browser import WebBrowser\n", + "from primaite.simulator.network.container import Network\n", + "from primaite.simulator.system.services.service import ServiceOperatingState\n", + "from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState\n", + "from primaite.simulator.system.services.database.database_service import DatabaseService\n", + "from primaite.simulator.system.applications.database_client import DatabaseClient\n", + "from primaite.simulator.network.hardware.nodes.network.firewall import Firewall\n", + "from primaite.game.game import PrimaiteGame\n", + "from primaite.simulator.sim_container import Simulation\n", + "import yaml\n", + "from pprint import pprint\n", + "from primaite.config.load import load, _EXAMPLE_CFG" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "use_case_7_config = load(_EXAMPLE_CFG/\"uc7_config.yaml\")\n", + "with open(file=_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['io_settings']['save_agent_logs'] = True # Save agent logs\n", + "env = PrimaiteGymEnv(env_config=use_case_7_config)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Table of Contents\n", + "\n", + "- [Network Description](#network-description)\n", + " - [Home Office](#network--home-office)\n", + " - [Internet](#network--internet)\n", + " - [Remote Site](#network--remote-site)\n", + " - [ST DMZ](#network--some_tech-dmz)\n", + " - [ST Main Site](#network--some_tech-main-site)\n", + " - [ST Head Office](#network--some_tech-main-site--some_tech-head-office-st_ho)\n", + " - [ST Human Resources](#network--some_tech-main-site--some_tech-human-resources-st_hr)\n", + " - [ST Data](#network--some_tech-main-site--some_tech-data-st_data)\n", + " - [ST Project A](#network--some_tech-main-site--some_tech-project-a-st_proj_a)\n", + " - [ST Project B](#network--some_tech-main-site--some_tech-project-b-st_proj_b)\n", + " - [ST Project C](#network--some_tech-main-site--some_tech-project-c-st_proj_c)\n", + "- [Agents](#agent-description)\n", + " - [Green POL](#agents--green-pattern-of-life-pol)\n", + " - [DatabaseService](#agents--green-pattern-of-life-pol--database-service)\n", + " - [web-server](#agents--green-pattern-of-life-pol--web-server)\n", + " - [Red Agent](#agents--red-agent)\n", + " - [TAP001](#agents--red-agent--threat-actor-profile-001-tap001)\n", + " - [TAP003](#agents--red-agent--threat-actor-profile-003-tap003)\n", + " - [Blue Agent](#agents--blue-agent)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## NETWORK DESCRIPTION\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "uc7_network = env.game.simulation.network\n", + "uc7_network.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "| Default Computer Software |\n", + "|------------------|\n", + "| DNS Client (Service) | \n", + "| NTP Client (Service) | \n", + "| web-browser (Application) |\n", + "| database-client (Application) | " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "| Default Server Software |\n", + "|------------------|\n", + "| DNS Client (Service) | \n", + "| NTP Client (Service) | " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | HOME OFFICE \n", + "\n", + "The HOME OFFICE (HOME) network simulates a generic home office that consist of the following:\n", + "\n", + "- A main LAN with two computers (`HOME-PUB-PC-1`/`2`) and a server (`HOME-PUB-SRV`) using default configurations.\n", + "- A switch (`HOME-PUB-SW-AS`) which connects the above hosts.\n", + "- Lastly, a router (`HOME-PUB-RT-DR`) which connects the home office to the wider networks. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Home Office PC 1 (HOME-PUB-PC-1)\n", + "home_pub_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-PC-1\")\n", + "home_pub_pc_1.show()\n", + "home_pub_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Home Office Router (HOME-PUB-RT-DR)\n", + "home_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-RT-DR\")\n", + "home_pub_rt_dr.show_nic()\n", + "home_pub_rt_dr.show_open_ports()\n", + "home_pub_rt_dr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | INTERNET (ISP)\n", + "\n", + "The internet (ISP) network intends to represent the internet. Currently, it's not feasible or possible for PrimAITE to simulate an internet service provider as well as the internet as a whole to a high degree of fidelity. Thus, in UC7, the \"internet\" refers to the following:\n", + "\n", + "- A router (`ISP-PUB-RT-BR`) which connects the all other UC7 LANS together. \n", + "- A server (`ISP-PUB-SRV-DNS`) which acts as the public DNS server that all PC's use. (Default IP of this host is *8.8.8.8*) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isp_pub_rt_br: Router = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-RT-BR\")\n", + "isp_pub_rt_br.show_nic()\n", + "isp_pub_rt_br.show_open_ports()\n", + "isp_pub_rt_br.acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isp_pub_srv_dns: Server = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-SRV-DNS\")\n", + "isp_pub_srv_dns.show_nic()\n", + "isp_pub_srv_dns_server: DNSServer = isp_pub_srv_dns.software_manager.software[\"dns-server\"]\n", + "isp_pub_srv_dns_server.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | REMOTE SITE \n", + "\n", + "The remote site (`REMOTE`) network is similar to the previously introduced `HOME` network but includes a firewall between the `ISP` and the `REMOTE` network:\n", + "\n", + "- A main LAN with two computers (`REM-PUB-PC-1`/`2`) and a server (`REM-PUB-SRV`) using default configurations.\n", + "- A switch (`REM-PUB-SW-AS`) which connects the above hosts.\n", + "- A router (`HOME-PUB-RT-DR`) which connects the home office to a firewall (`REM-PUB-FW`) which then connects back to the `INTERNET` network.\n", + "\n", + "By default the `REM-PUB-FW` will not block any traffic." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rem_pub_fw: Firewall = uc7_network.get_node_by_hostname(hostname=\"REM-PUB-FW\")\n", + "rem_pub_fw.show_nic()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# By default all of the `REM_PUB_FW` acls are configured to permit all traffic\n", + "rem_pub_fw.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH DMZ (ST DMZ)\n", + "\n", + "The `ST_DMZ` network is a small but important segment which sits between the `INTERNET` network (and thus the `REMOTE` and `HOME` networks as well) and the wider `SOME_TECH` main site networks. Additionally, accessible through the `ST_DMZ` firewall (`ST-PUB-FW`)'s DMZ port is a server which hosts a `web-server` that is accessible to all hosts in the UC7 network." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST DMZ Public Firewall (Permits all traffic by default)\n", + "st_pub_fw: Firewall = uc7_network.get_node_by_hostname(hostname=\"ST_PUB-FW\")\n", + "st_pub_fw.show_nic()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST DMZ Public web-server\n", + "st_dmz_pub_srv_web: Server = uc7_network.get_node_by_hostname(hostname=\"ST_DMZ-PUB-SRV-WEB\")\n", + "st_dmz_pub_srv_web.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE\n", + "\n", + "SOME TECH's main site is comprised of 6 different networks:\n", + "\n", + "| ST Network | Purpose |\n", + "|------------|---------|\n", + "| `ST_HO` | Some Tech's Head Office. |\n", + "| `ST_HR` | Some Tech's Human Resourcing department |\n", + "| `ST_DATA` | Contains the ST database and backup server (FTP) |\n", + "| `ST_PROJ_A` | Development Network |\n", + "| `ST_PROJ_B` | Development Network |\n", + "| `ST_PROJ_C` | Development Network |\n", + "\n", + "In order for hosts to communicate between each network and the wider internet, the main site utilises three routers' `ST_INTRA-PRV-RT-DR-1`, `ST_INTRA-PRV-RT-DR-2` and `ST_INTRA-PRV-RT-CR`.\n", + "\n", + "The `ST_INTRA-PRV-RT-DR-1` router is responsible for routing all traffic from the `ST_PROJ_A`/`B`/`C` networks whereas the `ST_INTRA-PRV-RT-DR-2` router routes all traffic from the `ST_HO`/`HR` networks. Both of which then forward all traffic to the main `ST_INTRA-PRV-RT-CR` router. \n", + "\n", + "This central router connects to the `ST_DMZ` firewall (`ST-PUB-FW`) as well as any traffic that is headed to the `ST_DATA` (the ST database and database storage) network.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr: Router = uc7_network.get_node_by_hostname(hostname=\"ST_INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_cr.route_table.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_dr_1: Router = uc7_network.get_node_by_hostname(hostname=\"ST_INTRA-PRV-RT-DR-1\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_dr_2: Router = uc7_network.get_node_by_hostname(hostname=\"ST_INTRA-PRV-RT-DR-2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH HEAD OFFICE (ST_HO)\n", + "\n", + "The some tech head office (`ST_HO`) is a simple LAN containing three computers with the default PC configuration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_head_office_private_pc_1: Computer = uc7_network.get_node_by_hostname(\"ST_HO-PRV-PC-1\")\n", + "st_head_office_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH HUMAN RESOURCES (ST_HR)\n", + "\n", + "Similarly, the some tech head human resources office (`ST_HR`) consisting of three default PC configurations computers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_human_resources_private_pc_2: Computer = uc7_network.get_node_by_hostname(\"ST_HR-PRV-PC-2\")\n", + "st_human_resources_private_pc_2.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH DATA (ST_DATA)\n", + "\n", + "The `ST_DATA` networks contains two servers pivotal to the daily operation of SOME_TECH.\n", + "\n", + "| Server | Purpose |\n", + "|--------|---------|\n", + "| `ST_DATA-PRV-SRV-DB` | Hosts the `database-service` that all `database-client` are configured to use. | \n", + "| `ST_DATA-PRV-SRV-STORAGE`| Acts as a storage server for the `ST_DATA-PRV-SRV-DB`. |" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_data_private_server_database: Server = uc7_network.get_node_by_hostname(\"ST_DATA-PRV-SRV-DB\")\n", + "st_data_private_server_database_service: DatabaseService = st_data_private_server_database.software_manager.software[\"database-service\"]\n", + "st_data_private_server_database.software_manager.show()\n", + "st_data_private_server_database.software_manager.file_system.show(full=True)\n", + "st_data_private_server_database_service.backup_server_ip" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_data_private_server_storage: Server = uc7_network.get_node_by_hostname(\"ST_DATA-PRV-SRV-STORAGE\")\n", + "st_data_private_server_storage.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH PROJECT A (ST_PROJ_A)\n", + "\n", + "All of the `ST_PROJ_A`/`B`/`C` project networks contain three computers and a switch which connects to the `ST_INTRA-PRV-RT-DR-1` router (as described previously)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1: Computer = uc7_network.get_node_by_hostname(\"ST_PROJ-A-PRV-PC-1\")\n", + "st_project_a_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH PROJECT B (ST_PROJ_B)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_b_private_pc_2: Computer = uc7_network.get_node_by_hostname(\"ST_PROJ-B-PRV-PC-2\")\n", + "st_project_b_private_pc_2.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### NETWORK | SOME_TECH MAIN SITE | SOME_TECH PROJECT C (ST_PROJ_C)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_c_private_pc_3: Computer = uc7_network.get_node_by_hostname(\"ST_PROJ-C-PRV-PC-3\")\n", + "st_project_c_private_pc_3.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use Case 7 Agent Description / Demonstration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### AGENTS | Green Pattern of Life (*PoL*)\n", + "\n", + "The UC7 green pattern of life refers to the traffic and behaviour generated by the default `32` different green agents. For each host on the UC7 network, two green agents will attempt to execute the `web-browser` and `database-client` applications and receive a positive or negative reward dependant on whenever they are successful or not.\n", + "\n", + "The table below gives a summary of the different green agent behaviour split between the different UC7 networks.\n", + "\n", + "| **Network** |**Agent Host** | **Application** | **Behaviour Type** | **Probabilities** | **Start Step** | **Start Variance** | **Frequency** | **Variance** | **Reward Impact** |\n", + "|:-----------:|:-------:|:---------------:|:------------------:|:-----------------:|:--------------:|:------------------:|:-------------:|:------------:|:------------------:|\n", + "| `HOME SITE` |`HOME-PUB-PC-1`| `database-client`| *Periodic* | |**4** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `HOME SITE` |`HOME-PUB-PC-2`| `database-client`| *Periodic* | |**8** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `HOME SITE` |`HOME-PUB-PC-1`/`2`| `web-browser` | *Probabilistic* |**20%** | | | | |**LOW** |\n", + "| `REMOTE SITE` |`REM-PUB-PC-1`| `database-client`| *Periodic* | |**12** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `REMOTE SITE` |`REM-PUB-PC-2`| `database-client`| *Periodic* | |**16** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `REMOTE SITE` |`REM-PUB-PC-1`/`2`| `web-browser` | *Probabilistic* |**20%** | | | | |**LOW** |\n", + "| `ST PROJECT A`/`B`/`C` |`ST_PROJ-*-PRV-PC-1`| `database-client`| *Periodic* | |**1** |**1** |**4** |**1** |**HIGH** |\n", + "| `ST PROJECT A`/`B`/`C` |`ST_PROJ-*-PRV-PC-1`| `web-browser` | *Probabilistic* |**40%** | | | | |**LOW** |\n", + "| `ST PROJECT A`/`B`/`C` |`ST_PROJ-*-PRV-PC-2`/`3`| `database-client`| *Periodic* | |**1** |**1** |**4** |**1** |**MEDIUM** |\n", + "| `ST PROJECT A`/`B`/`C` |`ST_PROJ-*-PRV-PC-2`/`3`| `web-browser` | *Probabilistic* |**20%** | | | | |**LOW** |\n", + "| `ST HEAD OFFICE` |`ST-HO-PRV-PC-1`| `web-browser` | *Probabilistic* |**60%** | | | | |**HIGH** |\n", + "| `ST HEAD OFFICE` |`ST-HO-PRV-PC-2`/`3`| `web-browser` | *Probabilistic* |**60%** | | | | |**MEDIUM** |\n", + "| `ST HUMAN RESOURCES` |`ST_HR-PRV-PC-1`| `web-browser` | *Probabilistic* |**60%** | | | | |**MEDIUM** |\n", + "| `ST HUMAN RESOURCES` |`ST_HR-PRV-PC-2`/`3`| `web-browser` | *Probabilistic* |**60%** | | | | |**LOW** |\n", + "\n", + "\n", + "For the full details on each green agent then please click on the drop-down menu below:\n", + "\n", + "
\n", + " UC7 Green Agent Full Details\n", + "\n", + " **ID** | **PoL Type** | **Description of Activity** | **Agent Name** | **Source Node** | **Source App / Service** | **Destination Node** | **Destination App / Service** | **Transport Protocol** | **Application Protocol** | **Behaviour Type** | **Probabilities** | **Start Step** | **Start Variance** | **Max Executions** | **Frequency** | **Variance** | **Reward Impact** \n", + ":------:|:------------:|:---------------------------------------------------:|:-------------------:|:------------------:|:------------------------:|:-----------------------:|:-----------------------------:|:----------------------:|:------------------------:|:------------------:|:-----------------:|:--------------:|:------------------:|:------------------:|:-------------:|:------------:|:-----------------:\n", + " 1 | AGENT | Home Worker accessing Some Tech database | HOME_WORKER-1 | HOME-PUB-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 4 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 2 | AGENT | Home Worker accessing Some Tech web pages | HOME_WORKER-1 | HOME-PUB-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 3 | AGENT | Home Worker accessing Some Tech database | HOME_WORKER-2 | HOME-PUB-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 8 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 4 | AGENT | Home Worker accessing Some Tech web pages | HOME_WORKER-2 | HOME-PUB-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 5 | AGENT | Remote Worker accessing Some Tech database | REMOTE_WORKER-1 | REM-PUB-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 12 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 6 | AGENT | Remote Worker accessing Some Tech web pages | REMOTE_WORKER-1 | REM-PUB-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 7 | AGENT | Remote Worker accessing Some Tech database | REMOTE_WORKER-2 | REM-PUB-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 16 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 8 | AGENT | Remote Worker accessing Some Tech web pages | REMOTE_WORKER-2 | REM-PUB-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 9 | AGENT | Senior Developer accessing Some Tech database | PROJ_A-SENIOR_DEV | ST_PROJ_A-PRV-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | HIGH \n", + " 10 | AGENT | Senior Developer accessing Some Tech web pages | PROJ_A-SENIOR_DEV | ST_PROJ_A-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 40% chance | | | | | | LOW \n", + " 11 | AGENT | Junior Developer accessing Some Tech database | PROJ_A-JUNIOR_DEV-1 | ST_PROJ_A-PRV-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 12 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_A-JUNIOR_DEV-1 | ST_PROJ_A-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 13 | AGENT | Junior Developer accessing Some Tech database | PROJ_A-JUNIOR_DEV-2 | ST_PROJ_A-PRV-PC-3 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 14 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_A-JUNIOR_DEV-2 | ST_PROJ_A-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 15 | AGENT | Senior Developer accessing Some Tech database | PROJ_B-SENIOR_DEV | ST_PROJ_B-PRV-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | HIGH \n", + " 16 | AGENT | Senior Developer accessing Some Tech web pages | PROJ_B-SENIOR_DEV | ST_PROJ_B-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 40% chance | | | | | | LOW \n", + " 17 | AGENT | Junior Developer accessing Some Tech database | PROJ_B-JUNIOR_DEV-1 | ST_PROJ_B-PRV-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 18 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_B-JUNIOR_DEV-1 | ST_PROJ_B-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 19 | AGENT | Junior Developer accessing Some Tech database | PROJ_B-JUNIOR_DEV-2 | ST_PROJ_B-PRV-PC-3 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 20 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_B-JUNIOR_DEV-2 | ST_PROJ_B-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 21 | AGENT | Senior Developer accessing Some Tech database | PROJ_C-SENIOR_DEV | ST_PROJ_C-PRV-PC-1 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | HIGH \n", + " 22 | AGENT | Senior Developer accessing Some Tech web pages | PROJ_C-SENIOR_DEV | ST_PROJ_C-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 40% chance | | | | | | LOW \n", + " 23 | AGENT | Junior Developer accessing Some Tech database | PROJ_C-JUNIOR_DEV-1 | ST_PROJ_C-PRV-PC-2 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 24 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_C-JUNIOR_DEV-1 | ST_PROJ_C-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 25 | AGENT | Junior Developer accessing Some Tech database | PROJ_C-JUNIOR_DEV-2 | ST_PROJ_C-PRV-PC-3 | Database Client | ST_DATA-PRV-SRV-DB | Database Service | TCP | PostgreSQL | PERIODIC | | 1 | 1 | 1000 | 4 | 1 | MEDIUM \n", + " 26 | AGENT | Junior Developer accessing Some Tech web pages | PROJ_C-JUNIOR_DEV-2 | ST_PROJ_C-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 20% chance | | | | | | LOW \n", + " 27 | AGENT | CEO accessing Some Tech web pages | CEO | ST_HO-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | HIGH \n", + " 28 | AGENT | CTO accessing Some Tech web pages | CTO | ST_HO-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | MEDIUM \n", + " 29 | AGENT | CFO accessing Some Tech web pages | CFO | ST_HO-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | MEDIUM \n", + " 30 | AGENT | Senior HR accessing Some Tech web pages | SENIOR_HR | ST_HR-PRV-PC-1 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | MEDIUM \n", + " 31 | AGENT | Junior HR accessing Some Tech web pages | JUNIOR_HR-1 | ST_HR-PRV-PC-2 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | HTTPS | PROBABILISTIC | 60% chance | | | | | | LOW \n", + " 32 | AGENT | Junior HR accessing Some Tech web pages | JUNIOR_HR-2 | ST_HR-PRV-PC-3 | Web Browser | ST_DMZ-PUB-SRV-WEB | Web Server | TCP | \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### AGENTS | Green *PoL* | database-client Agents Demo\n", + "\n", + "The `database-client` green agents will attempt to use their host's `database-client` application to make a simple connection to the `database-service` on the `ST_DATA-PRV-SRV-DB` host (these connections have no direct impact to the `database-service` or the `database.db` file itself).\n", + "\n", + "Additionally, `database-client` green agents are *Periodic* meaning they will attempt to use the database based on game time-steps. Specifically, these agents will begin on the time-step given in their `start step` setting and will then will reattempt on each subsequence timestep based on the `Frequency` setting. These settings are then randomised using the remaining `Start Variance` and `Variance` options (also given in timesteps). These values are used to *±* their respective base settings to ensure the green agents achieve a moderate amount of domain randomisation in each PrimAITE episode.\n", + "\n", + "For example, take a *Periodic* green agent set with a `start_step` of 4 and a `frequency` of **4** with a `start_variance` and a `variance` of **4** will cause a green agent to make it's first action on timestep $4 \\pm 1$ and then any subsequent actions every $4 \\pm 1$ timesteps afterwards.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset() # Resetting the simulation\n", + "home_pub_pc_1_database_green_agent = env.game.agents.get(\"HOME_WORKER-1-DB\")\n", + "for time_step in range(10):\n", + " env.step(0)\n", + " if not home_pub_pc_1_database_green_agent.history[time_step].action == 'DONOTHING':\n", + " print(home_pub_pc_1_database_green_agent.history[time_step])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_pc_1.software_manager.software[\"database-client\"].sys_log.show(last_n=10)\n", + "st_data_private_server_database.software_manager.software[\"database-service\"].sys_log.show(last_n=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### AGENTS | Green *PoL* | web-browser Agents Demo\n", + "\n", + "Unlike the `database-client` green agents, the `web-browser` green agents are *probabilistic*. These agents are quite simple; on every timestep a probability roll is made to determine whenever the agent acts. On a successful outcome the agent will attempt to execute the `web-browser` application which will then attempt to connect to the `ST-DMZ-PUB-SRV-WEB` host (Some Tech's web-server). On a unsuccessful outcome then the green agent will simply perform not action on this timestep.\n", + "\n", + "For example, a `web-browser` green agent with a `20%` chance has a $\\frac{1}{5}$ chance of actioning it's host's `web-browser` to access the `ST-DMZ-PUB-SRV-WEB` web-server. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset() # Resetting the simulation\n", + "home_pub_pc_1_web_browser_green_agent = env.game.agents.get(\"HOME_WORKER-1-WEB\")\n", + "for time_step in range(10):\n", + " env.step(0)\n", + " if not home_pub_pc_1_web_browser_green_agent.history[time_step].action == 'DONOTHING':\n", + " print(home_pub_pc_1_web_browser_green_agent.history[time_step])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-PC-1\")\n", + "home_pub_pc_1_web_browser_green_agent.logger.show()\n", + "home_pub_pc_1_web_browser: WebBrowser = home_pub_pc_1.software_manager.software[\"web-browser\"]\n", + "home_pub_pc_1_web_browser.sys_log.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### AGENTS | Red Agent\n", + "\n", + "For UC7, two new red agents have been developed which introduce a much more complex and realistic attacks in comparison to UC2's [data manipulation red agent](./Data-Manipulation-Customising-Red-Agent.ipynb) for the blue agent to defend against. These new red agents, or more commonly referred to `Threat Actor Profiles` (_TAPS_), utilise a series of different green, blue and red actions to simulate the different steps of a real-world attack.\n", + "\n", + "This notebook does not cover the red agents in much detail, hence it is highly recommended that readers should check out the respective TAP notebooks for a much more in-depth look at each TAP and their impacts.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### AGENTS | RED AGENT | Threat Actor Profile 001 (TAP001)\n", + "\n", + "This TAP aims to exfiltrate and then encrypt the `database.db` file on `ST_DATA-PRV-SRV-DB` host, whilst leaving the functionality of the database intact. Configured by default to start on the `ST_PROJ-A-PRV-PC-1` host, `TAP001` must first embed itself on the host, locate the target (`ST_DATA-PRV-SRV-DB`) through a series of NMAP scans, establish a connection to it's `C2Server` (`ISP-PUB-SRV-DNS` by default) and then finally attempt to exfiltrate and encrypt. \n", + "\n", + "If successful, the blue agent is configured to receive a serve negative reward and thus must prevent `TAP001` from ever reaching the target database. This could be through blocking it's connection to the target or it's `C2Server` via a carefully crafted ACL or perhaps through more a forceful approach such as shutting down the starting host.\n", + "\n", + "For more information on `TAP001` and it's impacts, [please refer to the TAP001 E2E notebook](./UC7-TAP001-Kill-Chain-E2E.ipynb) or for more blue agent involved demonstration refer to the [UC7 attack variants notebook](./UC7-attack-variants.ipynb) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# By default the `uc7_config.yaml` is setup to use TAP001\n", + "env.reset()\n", + "for _ in range(80):\n", + " env.step(action=0)\n", + "\n", + "uc7_tap001 = env.game.agents.get(\"attacker\")\n", + "uc7_tap001.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# TAP001 starting host\n", + "st_project_a_private_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"ST_PROJ-A-PRV-PC-1\")\n", + "st_project_a_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1.file_system.show(full=True)\n", + "isp_pub_srv_dns: Server = env.game.simulation.network.get_node_by_hostname(hostname=\"ISP-PUB-SRV-DNS\")\n", + "isp_pub_srv_dns.file_system.show(full=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Database Impact \n", + "st_data_private_server_database: Server = env.game.simulation.network.get_node_by_hostname(hostname=\"ST_DATA-PRV-SRV-DB\")\n", + "st_data_private_server_database.file_system.show(full=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### AGENTS | RED AGENT | Threat Actor Profile 003 (TAP003)\n", + "\n", + "Unlike `TAP001` more traditional representation of a threat actor, `TAP003` represents a malicious insider which leverages it's pre-existing knowledge to covertly add malicious access control lists (ACLs) to three different routers each of which affecting green agent traffic in a different way causing the blue agent to receive negative rewards. Thus, the blue agent must learn to leverage it's ability to remove rules and change credentials throughout the network to rectify the impacts of `TA003` and re-establish green POL and prevent TAP003 from accessing additional routers.\n", + "\n", + "The table below is a brief summary of the malicious acls added by `TAP003`\n", + "\n", + "|Target Router | Impact |\n", + "|----------------------|--------|\n", + "|`ST_INTRA-PRV-RT-DR-1`| Blocks all `POSTGRES_SERVER` that arrives at the `ST_INTRA-PRV-RT-DR-1` router. This rule will prevent all ST_PROJ_* hosts from accessing the database (`ST_DATA-PRV-SRV-DB`).|\n", + "|`ST_INTRA-PRV-RT-CR`| Blocks all `HTTP` traffic that arrives at the`ST_INTRA-PRV-RT-CR` router. This rule will prevent all SOME_TECH hosts from accessing the web-server (`ST-DMZ-PUB-SRV-WEB`)|\n", + "|`REM-PUB-RT-DR`| Blocks all `DNS` traffic that arrives at the `REM-PUB-RT-DR` router. This rule prevents any remote site works from accessing the DNS Server (`ISP-PUB-SRV-DNS`).|\n", + "\n", + "Lastly, it's highly recommended that users refer to the [TAP003 E2E notebook](./UC7-TAP003-Kill-Chain-E2E.ipynb) for further information or for the [UC7 attack variants notebook](./UC7-attack-variants.ipynb) demonstration of TAP003 defence." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Loading up the TAP003 UC7 config variant\n", + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['io_settings']['save_agent_logs'] = True # Saving agent logs\n", + "\n", + "env = PrimaiteGymEnv(env_config=cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# By default the `uc7_config.yaml` is setup to use TAP001\n", + "env.reset()\n", + "for _ in range(128):\n", + " env.step(action=0)\n", + "\n", + "uc7_tap003 = env.game.agents.get(\"attacker\")\n", + "uc7_tap003.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-DR-1\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-CR\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\").acl.show() " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## AGENTS | Blue Agent\n", + "\n", + "\n", + "\n", + "\n", + "In PrimAITE blue agent configuration is split into three separate sections, each responsible for a different part of the blue agent functionality.\n", + "\n", + "- `observation_space`\n", + "\n", + "The observation_space (or more commonly `OBS`) refers to the what simulation components the blue agent observes each `time_step`. A blue agent `OBS` can be configured to be as large the entire network or just an individual node. \n", + "\n", + "- `action_space`\n", + "\n", + "The action space configuration is used to set the actions available to the blue agent. The larger range of actions could be optimal for responding to a large set of different scenarios but could lead to longer training times. Crafting the perfect `action_space` is pivotal for creating an effective blue agent.\n", + "\n", + "- `reward_function`\n", + "\n", + "The `reward_function` section configures what type of reward (or penalisation) the blue agent will receive based on the current status of the simulation. A balanced and well crafted reward function is the path of success for any blue agent to learn what the best approach to scenario may be.\n", + "\n", + "\n", + "\n", + "_The remaining section of this notebook will cover the UC7 blue agent and the default aforementioned settings._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset() # Resetting the env\n", + "defender = env.game.agents.get(\"defender\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## AGENTS | Blue Agent | Observation Space (OBS)\n", + "\n", + "## NETWORK DESCRIPTION\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "_(Click to enlarge)_\n", + "\n", + "Represented as a hierarchy (i.e the same way as a dictionary) the blue agent OBS is separate into multiple sections.\n", + "\n", + "### Links\n", + "\n", + "The first section is Links, which is used to report the current network traffic load on every link in the network. Since the observation space is composed of categorical variables, the traffic values are converted from MBit/s to a category based on a percentage of the link's capacity.\n", + "\n", + "
Link Values Mapping\n", + "\n", + "|load observation|percent utilisation|\n", + "|--|--|\n", + "|0|exactly 0%|\n", + "|1|0-11%|\n", + "|2|11-22%|\n", + "|3|22-33%|\n", + "|4|33-44%|\n", + "|5|44-55%|\n", + "|6|55-66%|\n", + "|7|66-77%|\n", + "|8|77-88%|\n", + "|9|88-99%|\n", + "|10|exactly 100%|\n", + "\n", + "\n", + "
\n", + "\n", + "
\n", + " Link List\n", + " \n", + "|Link number|Endpoint A|Endpoint B|\n", + "|---|---|---|\n", + "|1|HOME-PUB-SW-AS:eth-1|HOME-PUB-RT-DR:eth-1 |\n", + "|2|HOME-PUB-SW-AS:eth-1|HOME-PUB-PC-1:eth-1 |\n", + "|3|HOME-PUB-SW-AS:eth-1|HOME-PUB-PC-2:eth-1 |\n", + "|4|HOME-PUB-SW-AS:eth-1|HOME-PUB-SRV:eth-1 | \n", + "|5|ISP-PUB-RT-BR:eth-1|HOME-PUB-RT-DR:eth-2 |\n", + "|6|ISP-PUB-RT-BR:eth-2|ISP-PUB-SRV-DNS:eth-1 |\n", + "|7|ISP-PUB-RT-BR:eth-3|REM-PUB-FW:eth-1 |\n", + "|8|REM-PUB-FW:eth-2|REM-PUB-RT-DR:eth-1 |\n", + "|9|REM-PUB-RT-DR:eth-2|REM-PUB-SW-AS:eth-1 |\n", + "|10|REM-PUB-SW-AS:eth-2|REM-PUB-PC-1:eth-1 |\n", + "|11|REM-PUB-SW-AS:eth-3|REM-PUB-PC-2:eth-1 | \n", + "|12|REM-PUB-SW-AS:eth-4|REM-PUB-SRV:eth-1 |\n", + "|13|ISP-PUB-RT-BR:eth-4|ST_PUB-FW:eth-1 |\n", + "|14|ST_PUB-FW:eth-3|ST_DMZ-PUB-SRV-WEB:eth-1 |\n", + "|15|ST_INTRA-PRV-RT-CR:eth-1|ST_PUB-FW:eth-2 |\n", + "|16|ST_INTRA-PRV-RT-CR:eth-2|ST_INTRA-PRV-RT-DR-1:eth-1 |\n", + "|17|ST_INTRA-PRV-RT-CR:eth-3|ST_INTRA-PRV-RT-DR-2:eth-1 | \n", + "|18|ST_INTRA-PRV-RT-CR:eth-4|ST_DATA-PRV-SW-AS:eth-1 |\n", + "|19|ST_HO-PRV-SW-AS:eth-1|ST_INTRA-PRV-RT-DR-2:eth-2 |\n", + "|20|ST_HO-PRV-SW-AS:eth-2|ST_HO-PRV-PC-1:eth-1 |\n", + "|21|ST_HO-PRV-SW-AS:eth-3|ST_HO-PRV-PC-2:eth-1 |\n", + "|22|ST_HO-PRV-SW-AS:eth-4|ST_HO-PRV-PC-3:eth-1 |\n", + "|23|ST_HR-PRV-SW-AS:eth-1|ST_INTRA-PRV-RT-DR-2:eth-3 |\n", + "|24|ST_HR-PRV-SW-AS:eth-2|ST_HR-PRV-PC-1:eth-1 |\n", + "|25|ST_HR-PRV-SW-AS:eth-3|ST_HR-PRV-PC-2:eth-1 |\n", + "|26|ST_HR-PRV-SW-AS:eth-4|ST_HR-PRV-PC-3:eth-1 |\n", + "|27|ST_DATA-PRV-SW-AS:eth-2|ST_DATA-PRV-SRV-STORAGE:eth-1 |\n", + "|28|ST_DATA-PRV-SW-AS:eth-3|ST_DATA-PRV-SRV-DB:eth-1 | \n", + "|29|ST_INTRA-PRV-RT-DR-1:eth-2|ST_PROJ-A-PRV-SW-AS:eth-1 |\n", + "|30|ST_PROJ-A-PRV-SW-AS:eth2|ST_PROJ-A-PRV-PC-1:eth-1|\n", + "|31|ST_PROJ-A-PRV-SW-AS:eth3|ST_PROJ-A-PRV-PC-2:eth-1 |\n", + "|32|ST_PROJ-A-PRV-SW-AS:eth4|ST_PROJ-A-PRV-PC-3:eth-1 | \n", + "|33|ST_INTRA-PRV-RT-DR-1:eth-3|ST_PROJ-B-PRV-SW-AS:eth-1 |\n", + "|34|ST_PROJ-B-PRV-SW-AS:eth2|ST_PROJ-B-PRV-PC-1:eth-1 |\n", + "|35|ST_PROJ-B-PRV-SW-AS:eth3|ST_PROJ-B-PRV-PC-2:eth-1 |\n", + "|36|ST_PROJ-B-PRV-SW-AS:eth4|ST_PROJ-B-PRV-PC-3:eth-1 | \n", + "|37|ST_INTRA-PRV-RT-DR-1:eth-4|ST_PROJ-C-PRV-SW-AS:eth-1 |\n", + "|38|ST_PROJ-A-PRV-SW-AS:eth2|ST_PROJ-C-PRV-PC-1:eth-1 |\n", + "|39|ST_PROJ-A-PRV-SW-AS:eth3|ST_PROJ-C-PRV-PC-2:eth-1 |\n", + "|40|ST_PROJ-A-PRV-SW-AS:eth4|ST_PROJ-C-PRV-PC-3:eth-1 |\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below prints out how the above link list table appears in the blue agent observation space" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "\n", + "obs, reward, _,_,info = env.step(0)\n", + "for i,x in obs['LINKS'].items():\n", + " print(i, x)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### HOSTS\n", + "\n", + "By default the blue agent is monitoring `3` different computers and `1` server:\n", + "\n", + "|Host label|hostname| services | applications | folders | files |\n", + "|:--:|:--:|:--:|:--:|:--:|:--:|\n", + "|HOST1|ST_PROJ-A-PRV-PC-1 | ftp-client | ransomware_script, database-client | downloads, exfiltration_folder | malware_dropper.ps1, database.db |\n", + "|HOST2|ST_PROJ-B-PRV-PC-2| ftp-client | ransomware-script, database-client | downloads, exfiltration_folder | malware_dropper.ps1, database.db |\n", + "|HOST3|ST_PROJ-C-PRV-PC-3| ftp-client | ransomware-script, database-client | downloads, exfiltration_folder | malware_dropper.ps1, database.db |\n", + "|HOST4|ST_DATA-PRV-SRV-DB||| database | database.db|\n", + "\n", + "\n", + "Each `time_step` these hosts report the following to the blue agent:\n", + "\n", + "- operating status \n", + "- number of file creations\n", + "- number of file deletions\n", + "- up to 4 installed services (operating status and health status)\n", + "- up to 2 installed applications (operating status, health status, and number of executions)\n", + "- up to 1 folder (health status)\n", + "- up to 1 file on the folders (health status and number of accesses)\n", + "- 1 network interface, including: \n", + " - operating status\n", + " - number of malicious network events detected\n", + " - amount of incoming and outgoing traffic on each protocol and port\n", + "\n", + "\n", + "\n", + "It's worth noting that for larger observation spaces, not every host will have the maximum number of files, folders, services, and applications configured for the observation space, so padding is used to ensure that the shape of each host observation is the same. The hosts appear in the order that they are defined in the config file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "\n", + "obs, reward, _,_,info = env.step(0)\n", + "for node_id, node_obs in obs['NODES'].items():\n", + " if not \"ROUTER\" in node_id: # filter out router OBS for now\n", + " print(node_id)\n", + " pprint(node_obs)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For any readers unfamiliar with the different enumeration values in any of the obs output (i.e what the `0`,`1`,`2`,`3` values represent) then please refer to the following tables for reference:\n", + "\n", + "
Host operating state category table\n", + "\n", + "|value|meaning|\n", + "|-|-|\n", + "|0|UNUSED|\n", + "|1|ON|\n", + "|2|OFF|\n", + "|3|BOOTING|\n", + "|4|SHUTTING DOWN|\n", + "\n", + "
\n", + "\n", + "
No. file creations/deletions category table\n", + "\n", + "|value|meaning|\n", + "|-|-|\n", + "|0|0|\n", + "|1|1-5|\n", + "|2|6-10|\n", + "|3|>10|\n", + "\n", + "
\n", + "\n", + "
Service operating state category table\n", + "\n", + "|operating_state|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|RUNNING|\n", + "|2|STOPPED|\n", + "|3|PAUSED|\n", + "|4|DISABLED|\n", + "|5|INSTALLING|\n", + "|6|RESTARTING|\n", + "\n", + "
\n", + "\n", + "
Application operating state category table\n", + "\n", + "|operating_state|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|RUNNING|\n", + "|2|CLOSED|\n", + "|3|INSTALLING|\n", + "\n", + "
\n", + "\n", + "
Service/Application health state category table\n", + "\n", + "|health_state|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|GOOD|\n", + "|2|FIXING|\n", + "|3|COMPROMISED|\n", + "|4|OVERWHELMED|\n", + "\n", + "
\n", + "\n", + "
Application number of executions category table\n", + "\n", + "|num_executions label|meaning|\n", + "|-|-|\n", + "|0|0|\n", + "|1|1-5|\n", + "|2|6-10|\n", + "|3|>10|\n", + "\n", + "
\n", + "\n", + "
Folder/file health status table\n", + "\n", + "|health_status label|meaning|\n", + "|-|-|\n", + "|0|UNUSED|\n", + "|1|GOOD|\n", + "|2|COMPROMISED|\n", + "|3|CORRUPT|\n", + "|4|RESTORING|\n", + "|5|REPAIRING|\n", + "\n", + "
\n", + "\n", + "
File number of access category table\n", + "\n", + "|num_access label|meaning|\n", + "|-|-|\n", + "|0|0|\n", + "|1|1-5|\n", + "|2|6-10|\n", + "|3|>10|\n", + "\n", + "
\n", + "\n", + "
NICs' operating_status category table\n", + "\n", + "|operating_status|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|ENABLED|\n", + "|2|DISABLED|\n", + "\n", + "
\n", + "\n", + "
NIC monitored traffic utilisation category table \n", + "\n", + "|load observation|percent utilisation|\n", + "|--|--|\n", + "|0|exactly 0%|\n", + "|1|0-11%|\n", + "|2|11-22%|\n", + "|3|22-33%|\n", + "|4|33-44%|\n", + "|5|44-55%|\n", + "|6|55-66%|\n", + "|7|66-77%|\n", + "|8|77-88%|\n", + "|9|88-99%|\n", + "|10|exactly 100%|\n", + "\n", + "\n", + "
\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Routers and Firewalls\n", + "\n", + "In addition, the agent can observe the list of Access Control List rules present on routers and firewalls.\n", + "\n", + "Routers have one ACL and a list of network interfaces (ports)\n", + "\n", + "Firewalls have six ACLs and three ports (ports are Internal/External/DMZ, with an Inbound and Outbound ACL for each)\n", + "\n", + "by default, the UC7 agent is configured to observe `3` different routers:\n", + "\n", + "- `ST_INTRA-PRV-RT-CR`\n", + "- `ST_INTRA-PRV-RT-DR-1`\n", + "- `REM-PUB-RT-DR`\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Port operating status category table\n", + "\n", + "|operating_status|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|ENABLED|\n", + "|2|DISABLED|\n", + "\n", + "
\n", + "\n", + "
ACL Permission category table\n", + "\n", + "|permission|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|ALLOW|\n", + "|2|DENY|\n", + "\n", + "
\n", + "\n", + "\n", + "
ACL source/destination IP ID category table\n", + "\n", + "|IP ID|IP address| Associated node|\n", + "|--|--|--|\n", + "|0|UNUSED|\n", + "|1|ALL IPs|\n", + "|2|192.168.1.2 | HOME-PUB-PC-1 |\n", + "|3|192.168.1.3 | HOME-PUB-PC-2 |\n", + "|4|192.168.1.4 | HOME-PUB-PC-SRV |\n", + "|5|192.168.20.2 | REM-PUB-PC-1 |\n", + "|6|192.168.20.3 | REM-PUB-PC-2 |\n", + "|7|192.168.20.4 | REM-PUB-SRV |\n", + "|8|192.168.100.2| ST_PUB_SRV_WEB_IP |\n", + "|9|192.168.200.2 | ST_HO-PRV-PC-1 |\n", + "|10|192.168.200.3 | ST_HO-PRV-PC-2 |\n", + "|11|192.168.200.4 | ST_HO-PRV-PC-3 |\n", + "|12|192.168.210.2 | ST_HR-PRV-PC-1 |\n", + "|13|192.168.210.3 | ST_HR-PRV-PC-2 |\n", + "|14|192.168.210.4 | ST_HR-PRV-PC-3 |\n", + "|15|192.168.220.2 | ST_DATA-PRV-SRV-STORAGE | \n", + "|16|192.168.220.3 | ST_DATA-PRV-SRV-DB |\n", + "|17|192.168.230.2 | PROJ-A-PRV-PC-1 |\n", + "|18|192.168.230.3 | PROJ-A-PRV-PC-2 |\n", + "|19|192.168.230.4 | PROJ-A-PRV-PC-3 |\n", + "|20|192.168.240.2 | PROJ-B-PRV-PC-1 |\n", + "|21|192.168.240.3 | PROJ-B-PRV-PC-2 |\n", + "|22|192.168.240.4 | PROJ-B-PRV-PC-3 |\n", + "|23|192.168.250.2 | PROJ-C-PRV-PC-1 |\n", + "|24|192.168.250.3 | PROJ-C-PRV-PC-2 |\n", + "|25|192.168.250.4 | PROJ-C-PRV-PC-3 |\n", + "\n", + "
\n", + "\n", + "
ACL Rule IP Address Wildcard category table\n", + "\n", + "|wildcard|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|None|\n", + "|2|0.0.0.1|\n", + "|3|0.0.0.255|\n", + "|4|0.0.255.255|\n", + "
\n", + "\n", + "
ACL Rule Port category table\n", + "\n", + "|permission|label|used for|\n", + "|--|--|--|\n", + "|0|UNUSED|padding|\n", + "|1|ANY||\n", + "|2|21|FTP|\n", + "|3|53|DNS|\n", + "|4|80|HTTP|\n", + "|5|123|NTP|\n", + "|6|5432|POSTGRES SERVER|\n", + "|7|22|SSH|\n", + "\n", + "
\n", + "\n", + "
ACL Rule Protocol category table\n", + "\n", + "|permission|label|\n", + "|--|--|\n", + "|0|UNUSED|\n", + "|1|ANY|\n", + "|2|ICMP|\n", + "|3|TCP|\n", + "|4|UDP|\n", + "\n", + "
\n", + "\n", + "
Firewall ports \n", + "\n", + "|index|label|\n", + "|--|--|\n", + "|1|external|\n", + "|2|internal|\n", + "|3|DMZ|\n", + "\n", + "
\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, _,_,info = env.step(0)\n", + "for node_id, node_obs in obs['NODES'].items():\n", + " if not \"HOST\" in node_id: # filter out hosts OBS and focus on ROUTER\n", + " print(node_id)\n", + " pprint(node_obs)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## AGENTS | Blue Agent | Action Space" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "#### `action_map`\n", + "\n", + "Numerically ordered, the `action_map` combines the options set out previously to define the actual details of what actions and the amount of actions that a blue agent can perform.\n", + "\n", + "For example, the snippet below details the first four actions the the default UC7 blue agent is setup with:\n", + "\n", + "```yaml\n", + " action_space:\n", + " action_map:\n", + " 0:\n", + " action: do-nothing\n", + " options: {}\n", + "\n", + " # |======================================|\n", + " # | ST_PROJ-A-PRV-PC-1 |\n", + " # |======================================|\n", + "\n", + " # ST_PROJ-A-PRV-PC-1 | node-os-scan\n", + " 1:\n", + " action: node-os-scan\n", + " options:\n", + " node_name: ST_PROJ-A-PRV-PC-1\n", + " # ST_PROJ-A-PRV-PC-1 | node-shutdown\n", + " 2:\n", + " action: node-shutdown\n", + " options:\n", + " node_name: ST_PROJ-A-PRV-PC-1\n", + " # ST_PROJ-A-PRV-PC-1 | node-startup\n", + " 3:\n", + " action: node-startup\n", + " options:\n", + " node_name: ST_PROJ-A-PRV-PC-1\n", + "```\n", + "\n", + "Converting the yaml snippet below we end up with the following:\n", + "\n", + "|Action Num | Action Type | Options|\n", + "|:---------:|:-----------:|:------:|\n", + "|0|**DONOTHING**|*n/a*|\n", + "|1|**NODE_OS_SCAN**|*node_name: ST_PROJ-A-PRV-PC-1*|\n", + "|2|**NODE_SHUTDOWN**|*node_name: ST_PROJ-A-PRV-PC-1*|\n", + "|3|**NODE_STARTUP**|*node_name: ST_PROJ-A-PRV-PC-1*|\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`0: DONOTHING`:\n", + "\n", + "The first action, `DONOTHING` is a default standard that all agents are setup to use by default in primAITE. Quite simply this action makes no impact to the simulation - literally does nothing. Although this obviously does not seem all that useful, in practice an agent with a small yet impactful actions (such as adding or removing ACL's rules) may find that performing no action may be better than risking a potentially detrimental one.\n", + "\n", + "Additionally, you may spotted the code snippet below dotted around this notebook and many others.\n", + "\n", + "```py\n", + " env.step(0)\n", + "```\n", + "\n", + "This code snippet is used to step forward in an PrimAITE episode and force the blue agent into performing no action which is very useful for demonstrating default simulation behaviour as well as the different impacts that the green and red agents have upon the environment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "env.step(0)\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "print(defender.history[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`1: NODE_OS_SCAN`:\n", + "\n", + "The first actual action that the blue agent can perform is scanning action. A variety of different scanning type actions (such as `NODE_APPLICATION_SCAN` or `NODE_FILE/FOLDER_SCAN`) which can be used to by the blue agent to gain a deeper understanding of the simulation state. Specifically, these actions will cause the blue agent's observations to update to the \"true\" `HEALTH_STATUS` of a simulation component. The `NODE_OS_SCAN` acts a combined version of all these scan type actions.\n", + "\n", + "For example, if a red agent corrupts and alters the health status of a file, the blue agent's observation space will not reflect this until the agent performs a `NODE_FILE_SCAN` on the newly corrupted file. It's worth noting that blue agents can be configured to see the true `HEALTH_STATUS` of software and files without needing to scan in the yaml. Although this may make it easier for an train and create an effective blue agent it could be seen as reducing the fidelity of the simulation.\n", + "\n", + "The code snippet below demonstrates an example where the blue agent uses the `NODE_OS_SCAN` action to reveal the true health status `ST_PROJ-A-PRV-PC-1`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, term, trunc, info = env.step(0)\n", + "print(defender.history[1])\n", + "print(f\"ftp-client (Prior Scan) OBS: {defender.observation_manager.current_observation['NODES']['HOST0']['SERVICES'][1]}\")\n", + "print(f\"database-client (Prior Scan) OBS: {defender.observation_manager.current_observation['NODES']['HOST0']['APPLICATIONS'][2]}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"ST_PROJ-A-PRV-PC-1\")\n", + "st_project_a_private_pc_1.software_manager.software[\"ftp-client\"].set_health_state(SoftwareHealthState.COMPROMISED)\n", + "st_project_a_private_pc_1.software_manager.software[\"database-client\"].set_health_state(SoftwareHealthState.COMPROMISED)\n", + "st_project_a_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set by the `node_scan_duration` option in the simulation `defaults` section, it takes **8** timesteps before the results of `node-os-scan` impact the blue agent's observation space." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Node OS Scan time step duration: {cfg[\"simulation\"][\"defaults\"][\"node_scan_duration\"]}')\n", + "env.step(1)\n", + "print(defender.history[2])\n", + "for _ in range(9):\n", + " obs, reward, term, trunc, info = env.step(0)\n", + "\n", + "print(f\"Current Simulation Time Step: {env.game.step_counter}\")\n", + "print(f\"ftp-client (Post Scan) OBS: {defender.observation_manager.current_observation['NODES']['HOST0']['SERVICES'][1]}\")\n", + "print(f\"database-client (Post Scan) OBS: {defender.observation_manager.current_observation['NODES']['HOST0']['APPLICATIONS'][2]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`2: NODE_SHUTDOWN`:\n", + "\n", + "The next action available is the `NODE_SHUTDOWN` action. This action quite is quite simple in comparison and literally attempts to shut down the target host given in the `options` settings which is set to `ST_PROJ-A-PRV-PC-1`. Shutting a PC down affects the `operating_status` of the host machine which the following snippets demonstrate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# `1` is equal to 'ON' in this case.\n", + "obs, reward, term, trunc, info = env.step(0)\n", + "print(f\"ST_PROJ-A-PRV-PC-1's (prior shutdown) operating state: {defender.observation_manager.current_observation['NODES']['HOST0']['operating_status']}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, term, trunc, info = env.step(2)\n", + "for _ in range(3):\n", + " env.step(0) # 3 second shut down time.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, term, trunc, info = env.step(0)\n", + "print(f\"ST_PROJ-A-PRV-PC-1's (post shutdown) operating state: {defender.observation_manager.current_observation['NODES']['HOST0']['operating_status']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`3: NODE_STARTUP`:\n", + "\n", + "Luckily, the blue agent can use it's third action `NODE_STARTUP` has been configured to bring the `ST_PROJ-A-PRV-PC-1` back up and running. Although, with the a three timestep reboot time.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs, reward, term, trunc, info = env.step(3)\n", + "\n", + "for _ in range(3):\n", + " env.step(0) # 3 second reboot time.\n", + " \n", + "obs, reward, term, trunc, info = env.step(0)\n", + "print(f\"ST_PROJ-A-PRV-PC-1's (prior shutdown) operating state: {defender.observation_manager.current_observation['NODES']['HOST0']['operating_status']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## AGENTS | Blue Agent | Reward Components\n", + "\n", + "In order to add context to the observation, the blue agent can be configured with different reward components. For any readers unfamiliar with PrimAITE rewards then user guide contains a good introduction as this notebook section will only cover the UC7 blue agent setup.\n", + "\n", + "For `UC7`, the blue agent is setup to receive a negative reward when the `database.db` file integrity is affected (i.e enters into a `COMPROMISED` state):\n", + "\n", + "```yaml\n", + " reward_function:\n", + " reward_components:\n", + " - type: DATABASE_FILE_INTEGRITY\n", + " weight: *HIGH_WEIGHT_IMPACT\n", + " options: \n", + " node_hostname: ST_DATA-PRV-SRV-DB \n", + " folder_name: database\n", + " file_name: database.db\n", + "```\n", + "\n", + "The blue agent's remaining reward function is comprised of **32** different ``SHARED_REWARD`` components. These rewards will grant the blue agent a positive or negative reward based on the current reward of the **32** green agents. The next code snippets The code snippets below demonstrate how the blue agent's reward is affected by simulation state." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(len(defender.reward_function.reward_components)):\n", + " try:\n", + " print(f\"Simulation State Reward: {defender.reward_function.reward_components[i][0].location_in_state}\")\n", + " except:\n", + " print(f\"Green Agent Shared Reward: {defender.reward_function.reward_components[i][0].config.agent_name}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In a perfect episode, with no red agent interference, the blue agent can expect the following reward:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"].pop(32) # removing red agent\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.reset()\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "for _ in range(128):\n", + " env.step(0)\n", + "defender.reward_function.total_reward" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The below code snippet shows what you'd expect the defender's total reward to be after TAP001 is left unchallenged:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.reset()\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "for _ in range(128):\n", + " env.step(0)\n", + "print(f\"Successful TAP001 & Blue Agent Reward: {defender.reward_function.total_reward}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next is what you'd expect the defender's total reward to be after TAP003 is left unchallenged:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['io_settings']['save_agent_actions'] = True # Saving syslogs\n", + " cfg['io_settings']['save_agent_logs'] = True # Saving agent logs\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.reset()\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "for _ in range(128):\n", + " env.step(0)\n", + "print(f\"Successful TAP003 & Blue Agent Reward: {defender.reward_function.total_reward}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, if we completely block any green traffic and set the database.db to `COMPROMISED` we can simulate the potential worst case situation for the blue agent (based off the default `reward_components`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.reset()\n", + "defender = env.game.rl_agents.get(\"defender\")\n", + "\n", + "# Corrupting and Disabling the database-service\n", + "st_data_private_server_database: Server = env.game.simulation.network.get_node_by_hostname(\"ST_DATA-PRV-SRV-DB\")\n", + "st_data_private_server_database_file = st_data_private_server_database.file_system.get_file(folder_name=\"database\", file_name=\"database.db\")\n", + "st_data_private_server_database_file.health_status = FileSystemItemHealthStatus.COMPROMISED\n", + "st_data_private_server_database.software_manager.software[\"database-service\"].operating_state = ServiceOperatingState.DISABLED\n", + "\n", + "# Shutting down the web-server\n", + "\n", + "st_dmz_pub_srv_web: Server = env.game.simulation.network.get_node_by_hostname(\"ST_DMZ-PUB-SRV-WEB\")\n", + "st_dmz_pub_srv_web.software_manager.software[\"web-server\"].operating_state = ServiceOperatingState.DISABLED\n", + "\n", + "# Shutting down the DNSServer\n", + "\n", + "isp_pub_srv_dns_server: Server = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-SRV-DNS\")\n", + "isp_pub_srv_dns_server.software_manager.software[\"dns-server\"].operating_state = ServiceOperatingState.DISABLED\n", + "\n", + "for _ in range(128):\n", + " env.step(0)\n", + "print(f\"Worst Case Episode Blue Agent Reward: {defender.reward_function.total_reward}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-TAP001-Kill-Chain-E2E.ipynb b/src/primaite/notebooks/UC7-TAP001-Kill-Chain-E2E.ipynb new file mode 100644 index 00000000..5ea67af3 --- /dev/null +++ b/src/primaite/notebooks/UC7-TAP001-Kill-Chain-E2E.ipynb @@ -0,0 +1,1844 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Mobile Malware Kill Chain - Ransomware Script Variant\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n", + "\n", + "**Threat Actor Profile (TAP):** 001
\n", + "**Kill Chain**: Mobile Malware - Ransomware Script Variant\n", + "\n", + "This notebook demonstrates TAP001 on the UC7 network infrastructure. In this scenario, a some_tech employee within the development project network plugs in his personal device. Whilst browsing, they encounter a ransomware virus which moves onto the host machine thus triggering the malware! \n", + "
\n", + "\n", + "This ransomware variant targets the database service's file directly, rather than the disrupting the database service, hence why in this scenario the data service still functions after becoming corrupted.\n", + "
\n", + "\n", + "The red attack intends to introduce realistic impacts to the observation space, such as files created/removed and applications installing mid-episode whilst still providing usable data for agent training." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Importing the necessary PrimAITE libraries\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "import yaml\n", + "from primaite.game.agent.scripted_agents.TAP001 import TAP001, MobileMalwareKillChain\n", + "from primaite.config.load import load, _EXAMPLE_CFG\n", + "from deepdiff.diff import DeepDiff" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Utility functions\n", + "\n", + "def display_obs_diffs(old, new, step_counter):\n", + " \"\"\"\n", + " Use DeepDiff to extract and display differences in old and new instances of\n", + " the observation space.\n", + "\n", + " :param old: observation space instance.\n", + " :param new: observation space instance.\n", + " :param step_counter: current step counter.\n", + " \"\"\"\n", + " print(\"\\nObservation space differences\")\n", + " print(\"-----------------------------\")\n", + " diff = DeepDiff(old, new)\n", + " print(f\"Step {step_counter}\")\n", + " for d,v in diff.get('values_changed', {}).items():\n", + " print(f\"{d}: {v['old_value']} -> {v['new_value']}\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Sections:**\n", + "1. Notebook Intro\n", + "2. Notebook Setup\n", + "3. Initial States\n", + "4. Attack Trigger\n", + "5. Post Attack States\n", + "6. Configurations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Intro** | **TAP001 - Mobile Malware Kill Chain** \n", + "\n", + "The TAP001 kill chain is comprised of 6 different stages.
The table below describes the impact of the current TAP001 implementation as well as the attack stage order.\n", + "\n", + "_DOWNLOAD_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|1|Download|**Starting Host** HOST:num_file_creations: HOST:FOLDER:FILES:* |The employee encounters a malicious payload through web browsing on his mobile phone, often disguised as legitimate content.|\n", + "\n", + "_INSTALL_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|2|Install | **Starting Host** HOST:FOLDER:FILES:num_access:|The malware is activated, either by the employee opening the file or automatically upon download, initiating its malicious functions.|\n", + "\n", + "_ACTIVATE_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|3|Activate | **Starting Host** HOST:APPLICATION:operating_status|The malware installs itself on the terminal, often seeking to gain persistence by embedding itself into system processes or startup routines.|\n", + "\n", + "_PROPAGATE_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|4|Propagate| **Multiple Hosts** HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:*|The malware attempts to spread to other systems or networks, looking for vulnerable services.|\n", + "\n", + "_COMMAND_AND_CONTROL_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|5|Command and Control|**Starting Host** HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* HOST:APPLICATIONS:APPLICATION:*|The malware establishes a connection to an external command and control (C&C) server, receiving instructions and possibly exfiltrating data.|\n", + "\n", + "_PAYLOAD_\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|6A|Corruption| **Target Host** HOST:FOLDERS:FOLDER:FILES:FILE:num_access HOST:num_file_creations HOST:num_file_deletions HOST:FOLDERS:FOLDER:FILES:FILE:health_status | The attacker configures the ransomware script to target the IP address of the discovered database, then it performs its intended malicious activities, allowing it to corrupt the Database whilst leaving the database service operable|\n", + "|6B|Exfiltration| **Starting Host** HOST:FOLDER:FILES:FILE HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* HOST:APPLICATIONS:APPLICATION:* | The attacker remotely logins into the target host and exfiltrates the database.db file onto the starting host which then relays this file back to the C2 Server.|\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Notebook Setup** | **Network Configuration:**\n", + "\n", + "This notebook uses the same network setup as UC7. Please refer to the main [UC7-E2E-Demo notebook for further reference](./UC7-E2E-Demo.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "

TAP001 Mobile Malware Kill Chain Demonstration - Ransomware Variant

\n", + "\n", + "_(Click to enlarge)_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['io_settings']['save_agent_actions'] = True # Saving attacker logs\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.game.simulation.network.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Setup** | Instantiating Relevant Simulation Objects\n", + "Simulation objects can be instantiated and called independently of agents via the environment.game.simulation (PrimAITE API).\n", + "\n", + "[Please refer to the main UC7 notebook for further details regarding agent implementations and the general UC7 scenario.](./example layout-E2E-Demo.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset() # resetting the environment\n", + "# The TAP001 Agent\n", + "tap001 = env.game.agents.get(\"attacker\")\n", + "# A potential starting client\n", + "starting_host = env.game.simulation.network.get_node_by_hostname('ST_PROJ-A-PRV-PC-1')\n", + "\n", + "# The database server which acts as the initial target of the ransomware kill-chain\n", + "database_server = env.game.simulation.network.get_node_by_hostname('ST_DATA-PRV-SRV-DB')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Prior To Attack** | Initial States:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first section of this notebook displays the relevant default Observation Space (OBS)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Initial State** | Starting Client:\n", + "The starting point of the ransomware infection. This is where the kill chain attack originates from." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host.show()\n", + "starting_host.file_system.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Initial State** | Database Server:\n", + "\n", + "TAP001 opts to attack the UC7 database server (`ST_DATA-PRV-SRV-DB`):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "database_server.software_manager.show()\n", + "database_server.file_system.show()\n", + "database_server.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Kill Chain** | Kill Chain Stage Demonstration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | NOT STARTED\n", + "\n", + "This stage indicates that the TAP001 hasn't begun it's kill chain. \n", + "TAP001 will begin the Mobile Malware Kill chain on the next execution step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset();\n", + "tap001 = env.game.agents.get(\"attacker\")\n", + "# The TAP001's Starting Client:\n", + "starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node)\n", + "# The TAP001's Database Server:\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST_DATA-PRV-SRV-DB')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.NOT_STARTED):\n", + " default_obs, _, _, _, _ = env.step(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | DOWNLOAD\n", + "\n", + "|Index|Action Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|1|DOWNLOAD|HOST:FOLDER:FILE:*|The `some_tech` employee encounters a malicious payload through web browsing on his mobile phone, often disguised as legitimate content.|\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + "

\n", + "\n", + "In this stage, TAP001 uses the **NODE_FOLDER_CREATE** and **NODE_FILE_CREATE** to create a file called ```\"malware_dropper.ps1\"``` within a ```\"Downloads\"``` folder.
\n", + "These actions are intended to simulate the malicious payload creating a ```ps1``` (A windows powershell script) malware dropper on the `SOME_TECH` employee's phone. \n", + "\n", + "Currently, PrimAITE cannot simulate hosts joining the simulation mid-episode thus we must treat `ST_PROJ-A-PRV-PC-1`'s as also including the employee's phone.
\n", + "From a narrative perspective, this could be explained as the employee plugging his phone into the `ST_PROJ-A-PRV-PC-1`.\n", + "\n", + "Additionally, it's worth noting that in the real world, malware droppers (small scripts or executables which download/install the malware after initially entering a host) use a variety of obfuscation methods to avoid detection.
For example, some malware droppers are concealed within legitimate files such as word document macros in order to trick a user into running the dropper.\n", + "\n", + "Currently, PrimAITE's simulation does not operate a high enough fidelity to faithfully represent these techniques and their impacts to warrant their inclusion. Thus the file has been named as ```\"malware_dropper\"``` to prevent any potential confusion." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.DOWNLOAD):\n", + " download_obs_impact, _, _, _, _ = env.step(0)\n", + "starting_host.file_system.show(full=True)\n", + "starting_host.file_system.show_num_files()\n", + "display_obs_diffs(default_obs, download_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | INSTALL\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|2|Install | HOST:FOLDER:FILES:num_access |The malware is activated, either by the employee opening the file or automatically upon download, initiating its malicious functions.|\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "In this stage, TAP001 uses the **NODE_FILE_ACCESS** to increase the number of accesses of the ```\"malware_dropper.ps1\"```.
\n", + "\n", + "These actions represent the employee executing malware dropper created in the previous stage. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.INSTALL):\n", + " install_obs_impact, _, _, _, _ = env.step(0)\n", + "\n", + "malware_file = starting_host.file_system.get_file(\"downloads\",\"malware_dropper.ps1\")\n", + "malware_file.show()\n", + "\n", + "display_obs_diffs(download_obs_impact, install_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | ACTIVATE\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|3|Activate | HOST:APPLICATION:operating_status |The malware installs itself on the terminal, often seeking to gain persistence by embedding itself into system processes or startup routines.|\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "In this stage, TAP001 uses the **NODE_APPLICATION_INSTALL** to install the ransomware application onto the starting host.
\n", + "\n", + "These actions represent the malware dropper successfully installing ransomware on the host machine. Similarly to the malware dropper, the ransomware currently implemented is intended to be a generic and OS agnostic ransomware which is not intended to represent any specific real world implementation. \n", + "\n", + "Please see the [Ransomware Notebook](./Ransomware-Kill-Chain-E2E.ipynb) for further information about the current implementation of the ransomware application.\n", + "\n", + "Future versions of PrimAITE intend to expand the capability of the ransomware application to more faithfully represent a real-world example; for example, a Trickbot variation such as Ryuk or Conti." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.ACTIVATE):\n", + " activate_obs_impact, _, _, _, _ = env.step(0)\n", + "starting_host.software_manager.show()\n", + "display_obs_diffs(install_obs_impact, activate_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | PROPAGATE\n", + "\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|4|Propagate|HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:*|The malware attempts to spread to other systems or networks, looking for vulnerable services.|\n", + "\n", + "In this stage, TAP001 uses **NODE_NMAP_PORT_SCAN**, **NODE_NMAP_PING_SCAN** and **NODE_NMAP_NETWORK_SERVICE_RECON** to scan the simulation in order to search for a valid database target.\n", + "\n", + "Unlike previous stages, the behaviour of this stage is dependant on the simulation and thus will perform differently dependant on the location of the target as well as the topology of the network. Specifically, the ```PROPAGATE``` stage uses three network enumeration actions and their action responses to populate its knowledge of the network.
\n", + "These actions represent the now infected `ST_PROJ-A-PRV-PC-1` searching the UC7 network for valid targets ransomware. \n", + "\n", + "For more information around how agent requests and responses work then the [request-response notebook can provide some useful insights](./Requests-and-Responses.ipynb).\n", + "\n", + "It's worth noting that the implementation of the ```PROPAGATE``` stage does not class routers and switches as valid targets and thus will not scan them.
\n", + "\n", + "_Currently, red agents have no way of probing the routing information of the simulation so must be provided the network addresses of the network.
See the mobile malware configuration option section of this notebook for further information_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | Scan walkthrough\n", + "\n", + "The next juypter cells of this notebook will go through each individual CAOS action that the TAP001 leverages to reach the target host as well as the OBS each action impacts.\n", + "\n", + "This section uses the following ```PROPAGATE``` relevant TAP001 settings:\n", + "```yaml\n", + " kill chain:\n", + " PROPAGATE:\n", + " probability: 1\n", + " scan_attempts: 20\n", + " repeat_scan: false\n", + " network_addresses:\n", + " - 192.168.230.0/29 # ST Project A\n", + " - 192.168.10.0/26 # Remote Site\n", + " - 192.168.20.0/30 # Remote DMZ\n", + " # - 192.168.240.0/29 # ST Project B\n", + " # - 192.168.250.0/29 # ST Project C\n", + " - 192.168.220.0/29 # ST Data (Contains Target)\n", + "```\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "\n", + "def propagate_obs_and_show(tap001: TAP001, previous_obs, propagate_obs_impact, timestep):\n", + " tap001._show_scan()\n", + " display_obs_diffs(previous_obs, propagate_obs_impact, timestep)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | ST_PROJ-A-PRV-PC-1\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ping Scan\n", + "for _ in range(5):\n", + " PROJ_A_SITE_ping, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=activate_obs_impact, propagate_obs_impact=PROJ_A_SITE_ping, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Recon Scan\n", + "for _ in range(5):\n", + " PROJ_A_SITE_recon, *_ = env.step(0);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, PROJ_A_SITE_ping, PROJ_A_SITE_recon, env.game.step_counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | REMOTE SITE\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ping Scan\n", + "for _ in range(5):\n", + " REMOTE_SITE_ping, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=activate_obs_impact, propagate_obs_impact=REMOTE_SITE_ping, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Recon Scan\n", + "for _ in range(5):\n", + " REMOTE_SITE_recon, *_ = env.step(0);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, REMOTE_SITE_ping, REMOTE_SITE_recon, env.game.step_counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | REMOTE DMZ\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ping Scan\n", + "for _ in range(5):\n", + " REMOTE_DMZ_ping, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=REMOTE_SITE_recon, propagate_obs_impact=REMOTE_DMZ_ping, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Recon Scan\n", + "for _ in range(5):\n", + " REMOTE_DMZ_recon, *_ = env.step(0);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=REMOTE_DMZ_ping, propagate_obs_impact=REMOTE_DMZ_recon, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Kill Chain** | PROPAGATE | SOME_TECH DATA \n", + "\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ping Scan\n", + "for _ in range(5):\n", + " ST_DATA_ping, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=REMOTE_DMZ_recon, propagate_obs_impact=ST_DATA_ping, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Recon Scan\n", + "for _ in range(5):\n", + " ST_DATA_recon, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "propagate_obs_and_show(tap001, previous_obs=ST_DATA_ping, propagate_obs_impact=ST_DATA_recon, timestep=env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for _ in range(5):\n", + " propagate_obs_impact, *_ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001._show_scan()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | COMMAND_AND_CONTROL\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|5|Command and Control| **Starting Host** HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* HOST:APPLICATIONS:APPLICATION:* |The malware establishes a connection to an external command and control (C&C) server, receiving instructions and possibly exfiltrating data.|\n", + "\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "\n", + "For further details please refer to the ``Command-and-Control-E2E-Demonstration`` notebook.\n", + "\n", + "_Note: The referenced notebook above uses the UC2 scenario for demonstration purposes, however all the OBS impacts and C2 suite functionality is equally applicable to UC7._\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.COMMAND_AND_CONTROL):\n", + " c_and_c_obs_impact, _, _, _, _ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "display_obs_diffs(propagate_obs_impact, c_and_c_obs_impact,env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | PAYLOAD - Corruption & Exfiltration\n", + "|Index|Attack Stage|OBS Impact|Narrative|\n", + "|-----|------------|----------|---------|\n", + "|6A|Corruption| **Target Host** HOST:FOLDERS:FOLDER:FILES:FILE:num_access HOST:num_file_creations HOST:num_file_deletions HOST:FOLDERS:FOLDER:FILES:FILE:health_status | The attacker configures the ransomware script to target the IP address of the discovered database, then it performs it's intended malicious activities, allowing it to corrupt the Database whilst leaving the database service operable|\n", + "|6B|Exfiltration| **Starting Host** HOST:FOLDER:FILES:FILE HOST:NICS:NIC:TRAFFIC:PROTOCOL:PORT:* HOST:APPLICATIONS:APPLICATION:* | The attacker remotely logins into the target host and exfiltrates the database.db file onto the starting host which then relays this file back to the C2 Server.|\n", + "\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap001.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap001.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap001.next_kill_chain_stage.name}\")\n", + "while(tap001.current_kill_chain_stage == MobileMalwareKillChain.PAYLOAD):\n", + " payload_obs_impact, _, _, _, _ = env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "target_host.file_system.show(full=True)\n", + "display_obs_diffs(c_and_c_obs_impact, payload_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can use the blue agent's NODE_FILE_SCAN action to scan the database file:\n", + "\n", + "```yaml\n", + "# ST_DATA-PRV-SRV-DB | node-file-scan | Scans the database.db file (health status)\n", + "49:\n", + " action: node-file-scan\n", + " options:\n", + " node_name: ST_DATA-PRV-SRV-DB\n", + " folder_name: database\n", + " file_name: database.db\n", + "```\n", + "\n", + "*You should notice a file `health_status` change to a value of 3*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(0)\n", + "post_scan_payload_impact, _, _, _, _ = env.step(49) # \n", + "display_obs_diffs(payload_obs_impact, post_scan_payload_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "target_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also see that the database.db file was successfully exfiltrated." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "c2_server = env.game.simulation.network.get_node_by_hostname(tap001.c2_settings['c2_server'])\n", + "starting_host.file_system.show(full=True)\n", + "c2_server.file_system.show(full=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Post Attack Impacts**\n", + "\n", + "Please refer to the [Ransomware E2E Notebook](./Ransomware-Kill-Chain-E2E.ipynb) for an in-depth look on the knock-on affects of the ransomware application." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Threat Actor Profile Settings" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All TAP's inherit the same general configurability shown in the following table:\n", + "\n", + "
TAP Agent Config Options \n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|start_step|The initial kill chain starting step.|Int|_Required_|\n", + "|frequency|The frequency of each kill chains starting steps.|Int|_Required_|\n", + "|variance| The timestep variance between frequency|Int|_Required_|\n", + "|repeat_kill_chain|Indicates whether the attack is repeated throughout the episode.|Bool|_Required_|\n", + "|repeat_kill_chain_stages|Indicates if the kill_chain stage should reset upon failure or retry.|Bool|_Required_|\n", + "|default_target_ip | The IP address of the target host |Str|_Required_|\n", + "|default_target_node|The Target Host|Str|_Required_|\n", + "|target_nodes|A list of Potential Target Hosts (database services) - Selected on per episode basis.|List|_Optional_|\n", + "|default_starting_node|The Starting Host|Str|_Required_|\n", + "|starting_nodes|A list of Potential Targets|List|_Optional_|\n", + "|kill_chain|TAP001 Specific Config (_See the next notebook section_)|Dict|_Required_|\n", + "|description|Free Text|Str|_Optional_|\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```yaml\n", + "\n", + " - ref: attacker\n", + " team: RED\n", + " type: TAP001\n", + " agent_settings:\n", + " start_step: 1\n", + " frequency: 3\n", + " variance: 0\n", + " repeat_kill_chain: false\n", + " repeat_kill_chain_stages: true\n", + " default_target_ip: *ST_SRV_DB_IP\n", + " default_starting_node: \"ST_PROJ-A-PRV-PC-1\"\n", + " # starting_nodes: [\"ST_PROJ-A-PRV-PC-1\", \"ST_PROJ-B-PRV-PC-2\", \"ST_PROJ-C-PRV-PC-3\"]\n", + " kill_chain:\n", + " ... # Next notebook section will cover this configuration option\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook assumes the reader is already familiar with the below `agent_settings` and won't provide any code snippet examples. \n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|start_step|The initial kill chain starting step.|Int|_Required_|\n", + "|frequency|The frequency of each kill chains starting steps.|Int|_Required_|\n", + "|variance| The timestep variance between frequency|Int|_Required_|\n", + "|repeat_kill_chain|Indicates whether the attack is repeated throughout the episode.|Bool|_Required_|\n", + "|repeat_kill_chain_stages|Indicates if the kill_chain stage should reset upon failure or retry.|Bool|_Required_|\n", + "|default_target_node|The Target Host|Str|_Required_|\n", + "|starting_nodes|A list of Potential Targets|List|_Optional_|\n", + "|kill_chain|_See the next notebook section_|Dict|_Required_||\n", + "\n", + "\n", + "If you're unfamiliar with the above then refer to the equivalent `agent_settings` section within the [TAP003 Kill Chain E2E Demonstration notebook](./TAP003-Kill-Chain-E2E.ipynb). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap001 = env.game.agents.get(\"attacker\")\n", + "for key,value in tap001.config.agent_settings:\n", + " if key == 'kill_chain':\n", + " pass\n", + " else:\n", + " print(f\"{key} : {value}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Kill Chain Settings\n", + "\n", + "Additionally, TAP001's Mobile Malware Kill Chain comes with some extra configuration options. \n", + "\n", + "These options can be configured to customise the behaviour of certain stages. \n", + "\n", + "The YAML snippet below is the current default configuration of the mobile malware kill chain:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``` YAML\n", + " - ref: attacker\n", + " team: RED\n", + " type: TAP001\n", + " agent_settings:\n", + " kill_chain:\n", + " ACTIVATE:\n", + " probability: 1\n", + " PROPAGATE:\n", + " probability: 1\n", + " scan_attempts: 20\n", + " repeat_scan: false\n", + " network_addresses:\n", + " - 192.168.230.0/29 # ST Project A\n", + " - 192.168.10.0/26 # Remote Site\n", + " - 192.168.20.0/30 # Remote DMZ\n", + " # - 192.168.240.0/29 # ST Project B\n", + " # - 192.168.250.0/29 # ST Project C\n", + " - 192.168.220.0/29 # ST Data (Contains Target)\n", + " COMMAND_AND_CONTROL:\n", + " probability: 1\n", + " keep_alive_frequency: 5\n", + " masquerade_port: HTTP\n", + " masquerade_protocol: TCP\n", + " c2_server_name: ISP-PUB-SRV-DNS\n", + " c2_server_ip: *PUBLIC_DNS_IP\n", + " PAYLOAD:\n", + " probability: 1\n", + " exfiltrate: true\n", + " corrupt: true\n", + " exfiltration_folder_name:\n", + " target_username: admin\n", + " target_password: admin\n", + " continue_on_failed_exfil: True\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Mobile Malware Kill Chain | General Settings" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "TAP001's ``ACTIVATE``, ``PROPAGATE``, ``COMMAND_AND_CONTROL`` and ``PAYLOAD`` stages's probability of success can be configured.\n", + "\n", + "Similarly to other TAPs, the argument given to probability is the chance of action performing that stage of kill chain successfully. \n", + "The argument given is expected to be between **0** - **1**. \n", + "\n", + "_(With '1' equalling 100% chance of 'success')_\n", + "\n", + "It's important to note that the probabilities of success are calculated within the game layer meaning that if an TAP fails because of probability then the TAP will perform a ``DONOTHING`` action for that step. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```yaml\n", + " kill_chain:\n", + " ACTIVATE:\n", + " probability: 1\n", + " PROPAGATE:\n", + " probability: 1\n", + " COMMAND_AND_CONTROL:\n", + " probability: 1\n", + " PAYLOAD:\n", + " probability: 1\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Mobile Malware Kill Chain | Propagate Stage\n", + "\n", + "TAP001's propagate step leverages the NMAP application to scan the network. This kill chain stage is a considerably more complex than the other stages and thus has more configuration options.\n", + "\n", + "
Propagate Configuration Settings \n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Action Probability - This is only calculated on the initial scan.|int|_Required_|\n", + "|network_addresses|The network-addresses that are scanned during the propagate step. Scanned in the order they are defined.|list[str]|_Required_|\n", + "|scan_attempts|The amount of permitted scan attempts.|int|_Required_|\n", + "|repeat_scan|Should the scan repeat if the target is not found within the given network address|bool|_optional_|\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Probability**\n", + "\n", + "***\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Action Probability |Int|_Required_|\n", + "\n", + "Similarly, to every other stage, probability is the chance of success of TAP001 has to successfully perform the Propagate Stage.\n", + "\n", + "However, it's important to note that the **probability of success is only calculated once**. \n", + "\n", + "After the first scan is performed, the TAP agent will perform the rest of the stage without trialing probability." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Network Addresses**\n", + "\n", + "***\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|network_addresses|The network-addresses that are scanned during the propagate step. Scanned in the order they are defined.|list[str]|_Required_|\n", + "\n", + "At present, TAPs cannot probe the simulation for routing information.\n", + "\n", + "Therefore, to scan multiple networks, the ``PROPAGATE`` stage must be provided a list of each network address.\n", + "\n", + "These network's are scanned in sequential order.\n", + "\n", + "For example; this notebook is configured with the following ``PROPAGATE`` network_address setting.\n", + "\n", + "| Network Address | Use Case 7 Subnet Name |\n", + "|-----------------|-------------------------|\n", + "| 192.168.10.0/26 | REMOTE-SITE |\n", + "| 192.168.20.0/30 | REMOTE-DMZ |\n", + "| 192.168.220.0/29 | SOME_TECH_DATA |\n", + "\n", + "\n", + "Which when represented in the yaml config is as follows:\n", + "\n", + "```yaml\n", + " kill_chain:\n", + " PROPAGATE:\n", + " probability: 1\n", + " scan_attempts: 20\n", + " repeat_scan: false\n", + " network_addresses:\n", + " - 192.168.230.0/29 # ST Project A\n", + " - 192.168.10.0/26 # Remote Site\n", + " - 192.168.20.0/30 # Remote DMZ\n", + " # - 192.168.240.0/29 # ST Project B\n", + " # - 192.168.250.0/29 # ST Project C\n", + " - 192.168.220.0/29 # ST Data (Contains Target)\n", + "```\n", + "Which is loaded into the TAP001 agent as the following list:\n", + "```Python\n", + "[\"192.168.230.0/29\", \"192.168.10.0/26\", \"192.168.20.0/30\", \"192.168.220.0/29\"]\n", + "```\n", + "\n", + "As PrimAITE expands and TAP agents are provided more ways of probing the simulation, then the ```PROPAGATE``` stage will be able to perform independently thus no longer requiring the network_address configuration option. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Scan Attempts**\n", + "\n", + "***\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|scan_attempts|The amount of permitted scan attempts.|int|_Required_|\n", + "\n", + "Simply, the ``scan_attempts`` configuration option indicates how many SCAN actions ``PROPAGATE`` stage is permitted to perform before the kill chain is considered to have failed. \n", + "\n", + "The ``scan_attempts`` option should mainly be kept higher than the length of the list provided in the previous ``network_address`` option.\n", + "\n", + "Currently, this setting is mainly used in conjunction with next setting: ```repeat_scan```.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Repeat Scan**\n", + "\n", + "***\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|repeat_scan|Should the scan repeat if the target is not found within the given network address|bool|_optional_|\n", + "\n", + "This boolean flag controls whether the ```PROPAGATE``` stage should repeat if the target is not found within the given ```network_addresses```.\n", + "\n", + "This setting used in conjunction with the ```permitted_scan``` option allows the Red Agent to continue scanning even if the target is not found. \n", + "\n", + "The network addresses will be selected at random after the first sequential scan(s) of the given ```network_addresses``` for further domain randomisation. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Removing the target network address. (Thus the target will never be found)\n", + "network_addresses = [\"192.168.10.0/26\", \"192.168.20.0/30\"]\n", + "\n", + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = True\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PROPAGATE']['network_addresses'] = network_addresses\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PROPAGATE']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PROPAGATE']['scan_attempts'] = 30\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PROPAGATE']['repeat_scan'] = True\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range (256):\n", + " env.step(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "\n", + "[1] _PrimAITE does not actually enforce agent type (Red/Green/Blue) specific actions_\n", + "\n", + "_However, some actions such as ``APPLICATION_EXECUTE`` and ``NETWORK_RECON_SCAN`` require an understanding of the simulation that is beyond the blue agent's current observation and thus are not suitable for use by reinforcement algorithms._\n", + "\n", + "_These actions are usually only leveraged by Green or Red agents; hence why they are commonly referenced as such._\n", + "\n", + "***" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Mobile Malware Kill Chain | Command and Control Stage\n", + "\n", + "TAP001's Command and Control stage leverages the C2 beacon which has it's own set of configuration options. In the case of TAP001 some of these settings are already pre-defined based on other settings such as ``target_node``. The table below details the currently available options.\n", + "\n", + "
Command and Control Configuration Settings \n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|probability | Action Probability - This is only calculated once at this stage. |Int | _Required_ |\n", + "|c2_server | What host should the C2 Beacon attempt to connect to as the chosen C2 Server |Str | _Required_ |\n", + "|keep_alive_frequency | How often should the C2 Beacon confirm its connection in timesteps. Defaults to 5 |Int | _Optional_ |\n", + "|masquerade_port | What port should the C2 traffic use? Defaults to TCP. |Str | _Optional_ |\n", + "|masquerade_protocol | What protocol should the C2 traffic masquerade as? Defaults to HTTP. |Str | _Optional_ |\n", + "
\n", + "\n", + "For further information around the configuration of the C2 beacon please refer to the ``Command-&-Control-E2E-Demonstration`` last section on configurability." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " # Adding the C2 Server to a different node (REM-PUB-PC-1)\n", + " cfg['simulation']['network']['nodes'][10].update(\n", + " {\"applications\": [\n", + " {\"type\": \"database-client\", \"options\":{\"db_server_ip\":\"192.168.220.3\"}},\n", + " {\"type\": \"web-browser\", \"options\":{\"target_url\": \"http://some_tech.com\"}},\n", + " {\"type\": \"c2-server\", \"options\":{\"listen_on_ports\":[21]}}]\n", + " })\n", + "\n", + " # Configuring the C2 stage to use the REM-PUB-PC-1 as it's C2 Server and to use a different masquerade port.\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"c2_server_name\"] = \"REM-PUB-PC-1\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"c2_server_ip\"] = \"192.168.20.2\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"keep_alive_frequency\"] = 3\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"masquerade_port\"] = \"FTP\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['COMMAND_AND_CONTROL'][\"masquerade_protocol\"] = \"TCP\"\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "# TAP001 runs for exactly 110 timesteps using default TAP settings.\n", + "for _ in range(110):\n", + " env.step(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cells below use .show() methods to show that the configuration options have successfully altered the C2's suite configuration. For example the C2 beacon's remote connection is now ``REM-PUB-PC-1``'s ip address which is ``192.168.20.2``." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST_PROJ-A-PRV-PC-1')\n", + "c2_beacon = starting_host.software_manager.software[\"c2-beacon\"]\n", + "c2_beacon.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "c2_server_host = env.game.simulation.network.get_node_by_hostname('REM-PUB-PC-1')\n", + "c2_server = c2_server_host.software_manager.software[\"c2-server\"]\n", + "c2_server.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Mobile Malware Kill Chain | Payload Stage\n", + "\n", + "
Payload Settings \n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|probability | Action Probability - This is only calculated once at this stage. |Int | _Required_ |\n", + "|corrupt | Should TAP001 launch the ransomware script against the target database? |Boolean | _Required_ |\n", + "|exfiltrate | Should TAP001 exfiltrate the target database.db file? |Boolean | _Required_ |\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Probability**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|probability | Action Probability - This is only calculated once at this stage. |Int | _Required_ |\n", + "\n", + "Similarly, to every other stage, probability is the chance of success of TAP001 has to successfully perform the Payload Stage.\n", + "\n", + "However, it's important to note that the **probability of success is only calculated once**. \n", + "\n", + "After the agent is successful once then the TAP agent will perform the rest of the stage without trialing any further." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Corrupt**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|corrupt | Should TAP001 launch the ransomware script against the target database? |Boolean | _Required_ |\n", + "\n", + "This option is a boolean value which indicates if TAP001 should launch the ransomware script against the target database. \n", + "\n", + "By default this is enabled but if users wish to disable the ransomware attack for training purposes then this value can be set to ``False`` which will prevent the installed ``RansomwareScript`` from launching at the final step.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Exfiltrate**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|---------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|exfiltrate | Should TAP001 exfiltrate the target database.db file? |Boolean | _Required_ |\n", + "\n", + "\n", + "Similar to ``corrupt``, this option is a boolean value which indicates if TAP001 should attempt to exfiltrate the database.db file.\n", + "\n", + "By default this is enabled but if users wish to disable the exfiltration for training purposes then this value can be set to ``False`` which will prevent the TAP001 agent from attempting to exfiltrate the database.db file." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_If both ``exfiltrate`` and ``corrupt`` options are enabled then the TAP001 agent will exfiltrate the database.db and then launch the ``RansomwareScript`` against the target._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Yaml Example:\n", + "\n", + "```yaml\n", + " kill_chain:\n", + " PAYLOAD:\n", + " probability: 1\n", + " exfiltrate: true\n", + " corrupt: true\n", + " exfiltration_folder_name:\n", + " target_username: admin\n", + " target_password: admin\n", + " continue_on_failed_exfil: True\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following code cells demonstrate how ``corrupt`` and ``exfiltrate`` can be enabled/disabled and their effects on the environment. After each example the file systems of the target and starting hosts are displayed. If ``corrupt`` is enabled then the target database file will be encrypted and if the ``exfiltrate`` option is enabled then the target database.db will be present within the starting host's file system." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"corrupt\"] = True\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"exfiltrate\"] = False\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST_PROJ-A-PRV-PC-1')\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST_DATA-PRV-SRV-DB')\n", + "target_host.file_system.show(full=True)\n", + "starting_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"corrupt\"] = False\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"exfiltrate\"] = True\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST_PROJ-A-PRV-PC-1')\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST_DATA-PRV-SRV-DB')\n", + "target_host.file_system.show(full=True)\n", + "starting_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"corrupt\"] = True\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"exfiltrate\"] = True\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST_PROJ-A-PRV-PC-1')\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST_DATA-PRV-SRV-DB')\n", + "target_host.file_system.show(full=True)\n", + "starting_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Payload Stage | Exfiltration Settings\n", + "\n", + "If TAP001 is configured to exfiltrate the database.db file then the following configuration options are relevant.\n", + "\n", + "
Exfiltration Settings \n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|-------------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|exfiltration_folder_name | The folder used to store the database.db file. Defaults to ``exfiltration_folder`` |Str | _Optional_ |\n", + "|target_username | The username used to login into a target node. Defaults to ``admin`` |Str | _Required_ |\n", + "|target_password | The password used to login into a target node. Defaults to ``admin`` |Str | _Required_ |\n", + "|continue_on_failed_exfil | Indicates if the attacker should encrypt the target even if the exfiltration fails |Bool | _Required_ |\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**exfiltration_folder_name**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|-------------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|exfiltration_folder_name | The folder used to store the database.db file. Defaults to ``exfiltration_folder``.|str | _Optional_ |\n", + "\n", + "Users are able to configure what folder the database.db is stored within after successful database exfiltration. For example, if this option was set to ``crown_jewels`` then the TAP001 agent would create a new folder on the starting node called ``crown_jewels`` and once the data-exfiltration is successful that folder will be populated with the ``database.db`` file from the target database. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"exfiltration_folder_name\"] = \"crown_jewels\"\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "starting_host = env.game.simulation.network.get_node_by_hostname('ST_PROJ-A-PRV-PC-1')\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST_DATA-PRV-SRV-DB')\n", + "target_host.file_system.show(full=True)\n", + "starting_host.file_system.show(full=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**target_username & target_password**\n", + "\n", + "***\n", + "\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|-------------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|target_username | The username used to login into a target node. Defaults to ``admin`` |Str | _required_ |\n", + "|target_password | The password used to login into a target node. Defaults to ``admin`` |Str | _required_ |\n", + "\n", + "These fields indicate what user credentials the TAP001 agent will use when attempting to exfiltrate the `database.db` file from the target." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Creating a new user for the TAP001 to use.\n", + "users = [{\n", + " \"username\": \"example_user\",\n", + " \"password\": \"example_pass\",\n", + " \"is_admin\": \"False\",\n", + "}]\n", + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['simulation']['network']['nodes'][28].update({\"users\":users}) # Adding this new user to the target\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"target_username\"] = \"example_user\" # Setting TAP001 to use the new user credentials.\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"target_password\"] = \"example_pass\"\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "for _ in range(110):\n", + " env.step(0)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "target_host = env.game.simulation.network.get_node_by_hostname('ST_DATA-PRV-SRV-DB')\n", + "target_host.user_manager.show()\n", + "target_host.file_system.show()\n", + "tap001.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**continue_on_failed_exfil**\n", + "\n", + "***\n", + "|Option Field | Meaning |Expected Type |Required/Optional|\n", + "|-------------------------|------------------------------------------------------------------------------------|---------------|-----------------|\n", + "|continue_on_failed_exfil | Indicates if the attacker should encrypt the target even if the exfiltration fails |Bool | _Required_ |\n", + "\n", + "\n", + "This option will affect how the TAP001 agent responds to failing the exfiltration. If this option is set to `True` then the TAP001 agent will attempt the final attack against the database even if the `database.db` exfiltration attempt is unsuccessful. Likewise, if this option is set to `False` then the TAP001 agent will consider it's attack failed if it cannot complete the exfiltration.\n", + "\n", + "The code cells below demonstrate the differences by using the blue agent to change the user account details on the target database before the exfiltration occurs using action 50\n", + "\n", + "```yaml\n", + " # ST_DATA-PRV-SRV-DB | node-account-change-password | Changes the password of a user account\n", + " 50:\n", + " action: node-account-change-password\n", + " options:\n", + " node_name: ST_DATA-PRV-SRV-DB\n", + " username: admin # default account\n", + " current_password: admin # default password\n", + " new_password: thr33_alert_wolv3z # A more 'secure' password\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we set the `continue_on_failed_exfil` to true we can see that the despite the exfiltration failing the `database.db` still ends up corrupted by the end of the attack." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"continue_on_failed_exfil\"] = True\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "\n", + "# Changing the target database credentials:\n", + "env.step(50)\n", + "\n", + "# Finishing the episode:\n", + "for _ in range(127):\n", + " env.step(0)\n", + " \n", + "tap001.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The exfiltration was not successful\n", + "starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node)\n", + "\n", + "starting_host.file_system.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Yet the target database.db is still corrupt.\n", + "\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST_DATA-PRV-SRV-DB')\n", + "target_host.file_system.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Whereas now if we set `continue_on_failed_exfil` to false we can see that the attack failed and the database health status remains `GOOD`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PAYLOAD'][\"continue_on_failed_exfil\"] = False\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "\n", + "# Changing the target database credentials:\n", + "env.step(50)\n", + "\n", + "# Finishing the episode:\n", + "for _ in range(127):\n", + " env.step(0)\n", + " \n", + "tap001.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# The exfiltration was not successful\n", + "starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node)\n", + "\n", + "starting_host.file_system.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# And the target database.db remains healthy.\n", + "\n", + "target_host = env.game.simulation.network.get_node_by_hostname('ST_DATA-PRV-SRV-DB')\n", + "target_host.file_system.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-TAP003-Kill-Chain-E2E.ipynb b/src/primaite/notebooks/UC7-TAP003-Kill-Chain-E2E.ipynb new file mode 100644 index 00000000..306e070a --- /dev/null +++ b/src/primaite/notebooks/UC7-TAP003-Kill-Chain-E2E.ipynb @@ -0,0 +1,1714 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Backdoor & Vulnerability Creation Kill Chain\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n", + "\n", + "**Threat Actor Profile (TAP):** 003
\n", + "**Kill Chain**: Backdoor & Vulnerability Creation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook demonstrates a new UC7 Kill Chain which aims to represent a different style of attack in comparison to the mobile malware kill chain (TAP001).\n", + "Kill chains aim to represent a potential real world cyber attack. In this scenario an malicious `SOME_TECH` admin (TAP003) leverages his legitimate credentials and permissions to create purposeful backdoors, establish footholds and use other legitimate features in malicious ways.\n", + "
\n", + "\n", + "In this version - this scenario is limited in scope. TAP003 opts to alter user accounts and implement malicious ACL rule by using the ``terminal`` service to SSH into target routers. These ACLs block green traffic which trigger a negative reward.\n", + "
\n", + "\n", + "This kill chain intends to introduce a new UC7 attack which is both realistic but also dissimilar to other kill chains." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "# Importing the necessary primAITE libraries\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite.simulator.system.applications.database_client import DatabaseClient\n", + "from primaite.simulator.system.services.dns.dns_client import DNSClient\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "from primaite.simulator.system.applications.web_browser import WebBrowser\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.game.agent.scripted_agents.TAP003 import TAP003, InsiderKillChain, InsiderKillChainOptions\n", + "from primaite.config.load import load, _EXAMPLE_CFG\n", + "from pprint import pprint\n", + "from deepdiff.diff import DeepDiff\n", + "from prettytable import PrettyTable\n", + "import yaml" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Utility functions.\n", + "\n", + "def print_agent_actions_except_do_nothing(agent_name):\n", + " \"\"\"Get the agent's action history, filter out `do-nothing` actions, print relevant data in a table.\"\"\"\n", + " table = PrettyTable()\n", + " table.field_names = [\"Step\", \"Action\", \"Node\", \"Application\", \"Target IP\", \"Response\"]\n", + " print(f\"Episode: {env.episode_counter}, Actions for '{agent_name}':\")\n", + " for item in env.game.agents[agent_name].history:\n", + " if item.action == \"do-nothing\":\n", + " continue\n", + "\n", + " node, application, target_ip = \"N/A\", \"N/A\", \"N/A\",\n", + "\n", + " if item.action.startswith(\"node-nmap\"):\n", + " node = item.parameters['source_node']\n", + " application = \"nmap\"\n", + " target_ip = str(item.parameters['target_ip_address'])\n", + " target_ip = (target_ip[:25]+'...') if len(target_ip)>25 else target_ip # truncate long string\n", + "\n", + " elif item.action == \"router-acl-add-rule\":\n", + " node = item.parameters.get(\"router_name\")\n", + " elif item.action == \"node-send-remote-command\":\n", + " node = item.parameters.get(\"node_name\")\n", + " target_ip = item.parameters.get(\"remote_ip\")\n", + " application = item.parameters.get(\"command\")\n", + " elif item.action == \"node-session-remote-login\":\n", + " node = item.parameters.get(\"node_name\")\n", + " target_ip = item.parameters.get(\"remote_ip\")\n", + " application = \"user-manager\"\n", + " elif item.action.startswith(\"c2-server\"):\n", + " application = \"c2-server\"\n", + " node = item.parameters.get('node_name')\n", + " elif item.action == \"configure-c2-beacon\":\n", + " application = \"c2-beacon\"\n", + " node = item.parameters.get('node_name')\n", + "\n", + " else:\n", + " if (node_id := item.parameters.get('node_id')) is not None:\n", + " node = env.game.agents[agent_name].action_manager.node_names[node_id]\n", + " if (application_id := item.parameters.get('application_id')) is not None:\n", + " application = env.game.agents[agent_name].action_manager.application_names[node_id][application_id]\n", + " if (application_name := item.parameters.get('application_name')) is not None:\n", + " application = application_name\n", + "\n", + " table.add_row([item.timestep, item.action, node, application, target_ip, item.response.status])\n", + "\n", + " print(table)\n", + " print(\"(Any do-nothing actions are omitted)\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def display_obs_diffs(old, new, step_counter):\n", + " \"\"\"\n", + " Use DeepDiff to extract and display differences in old and new instances of\n", + " the observation space.\n", + "\n", + " :param old: observation space instance.\n", + " :param new: observation space instance.\n", + " :param step_counter: current step counter.\n", + " \"\"\"\n", + " print(\"\\nObservation space differences\")\n", + " print(\"-----------------------------\")\n", + " diff = DeepDiff(old, new)\n", + " print(f\"Step {step_counter}\")\n", + " for d,v in diff.get('values_changed', {}).items():\n", + " print(f\"{d}: {v['old_value']} -> {v['new_value']}\")\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Sections:**\n", + "1. Notebook Intro\n", + "2. Notebook Setup\n", + "3. Prior To Attack\n", + "4. Kill Chain Stage Demonstration\n", + "5. Attack Configurations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Intro** | **Backdoor & Vulnerability Creation Kill Chain Intro** \n", + "\n", + "TAP003's kill chain is comprised of a variety of blue actions which are leveraged in unusual ways. This includes introducing malicious ACL's which block green traffic and installing and execute green applications in order to simulate unusual green pattern of life. The rest of this notebook will go through each step in more detail whilst demonstrating the impacts that each step has on both observation and simulation behaviour.\n", + "\n", + "_Reconnaissance - DONOTHING CAOS Action_\n", + "\n", + "|Index | Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|-------------|-----------|\n", + "|1|Reconnaissance|*No Direct Impact*|TAP003 is passively investigating sensitive systems, data and access control mechanisms.|\n", + "\n", + "_Planning - DONOTHING CAOS Action_\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|2|Planning| **No current impact**|TAP003 is devising a plan to exploit their elevated privileges.|\n", + "\n", + " _Access - DONOTHING CAOS Action__\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|3|Access|**No current impact** |TAP003 uses their legitimate credentials to access the access control settings.|\n", + "\n", + " _Manipulation - HOST:SESSIONS_SEND_REMOTE_COMMAND -> HOST:ACCOUNTS:CHANGE:PASSWORD CAOS ACTION_\n", + " \n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|4|Manipulation| **Target Host(s)** HOST::SESSIONS:REMOTE |TAP003 exploits their insider knowledge/privilege to implement changes for sabotage.|\n", + "\n", + " _Exploit - FIREWALL:ACL:add_rule CAOS ACTION_\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|5|Exploit| **Target Host(s)** FIREWALL:ACL:INTERNAL/EXTERNAL:*|TAP003 exploits their insider knowledge/privilege to implement changes for sabotage.|\n", + "\n", + "_Only the initial five steps are represented in the this version of this kill-chain._
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Notebook Setup** | **Network Configuration:**\n", + "\n", + "This notebook uses the same network setup as UC7. \n", + "\n", + "Please refer to the main [UC7-E2E-Demo notebook for further reference](./UC7-E2E-Demo.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "_(Click to enlarge)_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_agent_logs'] = True\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + "env = PrimaiteGymEnv(env_config=cfg)\n", + "env.game.simulation.network.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Notebook Setup** | Instantiating Relevant Simulation Objects\n", + "The cell below sets up the relevant simulation and agent objects for the kill chain demonstration.\n", + "\n", + "\n", + "The following cell resets the environment, instantiates TAP003 and it's selected starting/target hosts." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "env.reset() # resetting the environment\n", + "# The TAP003 Agent\n", + "tap003 = env.game.agents['attacker']\n", + "tap003.logger.logger.setLevel(\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Prior To Attack** | Initial States:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first section of this notebook displays the initial simulation state of TAP003's starting and target nodes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Initial State** | Starting Host:\n", + "TAP003's initial starting point.\n", + "\n", + "TAP003's starting host does not cause any MNE (Malicious Network Events) in it's current implementation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "# TAP003's Starting Client:\n", + "starting_host = env.game.simulation.network.get_node_by_hostname(tap003.starting_node)\n", + "print(f\"Starting host:\")\n", + "starting_host.show()\n", + "starting_host.software_manager.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Initial State** | Target Hosts\n", + "\n", + "TAP003's selected target hosts at current the only valid targets for TAP003 are Router/Firewall type nodes\n", + "\n", + "Code snippet below shows the default OBS of the target routers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "# TAP003's target routers\n", + "\n", + "st_intra_prv_rt_cr: Router = env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_dr_1: Router = env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-DR-1\")\n", + "rem_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\")\n", + "\n", + "st_intra_prv_rt_cr.acl.show()\n", + "st_intra_prv_rt_dr_1.acl.show()\n", + "rem_pub_rt_dr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Kill Chain** | Kill Chain Stage Demonstration \n", + "\n", + "For the initial kill chain demonstration, TAP003 is configured with the following yaml snippet:\n", + "\n", + "```yaml\n", + " - ref: attacker\n", + " team: RED\n", + " type: tap-003\n", + " observation_space: {}\n", + " action_space: {}\n", + " agent_settings:\n", + " start_step: 1\n", + " frequency: 3\n", + " variance: 0\n", + " repeat_kill_chain: false\n", + " repeat_kill_chain_stages: true\n", + " default_starting_node: \"ST_PROJ-A-PRV-PC-1\"\n", + " starting_nodes:\n", + " # starting_nodes: [\"ST_PROJ-A-PRV-PC-1\", \"ST_PROJ-B-PRV-PC-2\", \"ST_PROJ-C-PRV-PC-3\"]\n", + " kill_chain:\n", + " PLANNING:\n", + " probability: 1\n", + " starting_network_knowledge:\n", + " credentials:\n", + " ST_PROJ-A-PRV-PC-1:\n", + " username: admin\n", + " password: admin\n", + " ST_PROJ-B-PRV-PC-2:\n", + " username: admin\n", + " password: admin\n", + " ST_PROJ-C-PRV-PC-3:\n", + " username: admin\n", + " password: admin\n", + " ST_INTRA-PRV-RT-DR-1:\n", + " ip_address: 192.168.230.1\n", + " username: admin\n", + " password: admin\n", + " ST_INTRA-PRV-RT-CR:\n", + " ip_address: 192.168.160.1\n", + " username: admin\n", + " password: admin\n", + " REM-PUB-RT-DR:\n", + " ip_address: 192.168.10.2\n", + " username: admin\n", + " password: admin\n", + " ACCESS:\n", + " probability: 1\n", + " MANIPULATION:\n", + " probability: 1\n", + " account_changes:\n", + " - host: ST_INTRA-PRV-RT-DR-1\n", + " ip_address: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1\n", + " action: change_password\n", + " username: admin\n", + " new_password: \"red_pass\"\n", + " - host: ST_INTRA-PRV-RT-CR\n", + " ip_address: 192.168.160.1 # ST_INTRA-PRV-RT-CR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " - host: REM-PUB-RT-DR\n", + " ip_address: 192.168.10.2 # REM-PUB-RT-DR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " EXPLOIT:\n", + " probability: 1\n", + " malicious_acls:\n", + " - target_router: ST_INTRA-PRV-RT-DR-1\n", + " position: 1\n", + " permission: DENY\n", + " src_ip: ALL\n", + " src_wildcard: 0.0.255.255\n", + " dst_ip: ALL\n", + " dst_wildcard: 0.0.255.255\n", + " src_port: POSTGRES_SERVER\n", + " dst_port: POSTGRES_SERVER\n", + " protocol_name_name: TCP\n", + " - target_router: ST_INTRA-PRV-RT-CR\n", + " position: 1\n", + " permission: DENY\n", + " src_ip: ALL\n", + " src_wildcard: 0.0.255.255\n", + " dst_ip: ALL\n", + " dst_wildcard: 0.0.255.255\n", + " src_port: HTTP\n", + " dst_port: HTTP\n", + " protocol_name_name: TCP\n", + " - target_router: REM-PUB-RT-DR\n", + " position: 1\n", + " permission: DENY\n", + " src_ip: ALL\n", + " src_wildcard: 0.0.255.255\n", + " dst_ip: ALL\n", + " dst_wildcard: 0.0.255.255\n", + " src_port: DNS\n", + " dst_port: DNS\n", + " protocol_name_name: TCP\n", + "\n", + "```\n", + "_Further information & guidance around configuring TAP003 can be found within the last section of this notebook._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | NOT STARTED\n", + "This is the default state of the TAP003 Agent. This stage indicates that the TAP003 agent has not begun it's kill chain.\n", + "If TAP003 is in this stage then on the next execution step the kill chain will begin." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003 = env.game.agents['attacker']\n", + "\n", + "\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.NOT_STARTED):\n", + " default_obs, _, _, _, _ = env.step(0)\n", + "starting_host.software_manager.show()\n", + "\n", + "env.step(0);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | RECONNAISSANCE\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|1|RECONNAISSANCE|*No Direct Impact*|TAP003 is identifying Sensitive systems, data and access control mechanisms in legitimate ways.|\n", + "\n", + "Currently, this stage in the kill chain is implemented via the 'DONOTHING' CAOS action." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.RECONNAISSANCE):\n", + " reconnaissance_obs_impact, _, _, _, _ = env.step(0)\n", + "\n", + "current_host = env.game.simulation.network.get_node_by_hostname(tap003.current_host)\n", + "current_host.software_manager.show()\n", + "display_obs_diffs(default_obs, reconnaissance_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | PLANNING\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|2|PLANNING| *No Direct Impact*| HOST:APPLICATION|TAP003 is devising a plan to exploit their elevated privileges.|\n", + "\n", + "Currently, the TAP003 agent does not perform any actions at this stage of the kill chain." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "pprint(\"---Starting Client State----\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.PLANNING):\n", + " planning_obs_impact, _, _, _, info = env.step(0);\n", + "current_host.software_manager.show()\n", + "display_obs_diffs(reconnaissance_obs_impact, planning_obs_impact, env.game.step_counter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | ACCESS\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|3|Access|_DONOTHING CAOS Action_|TAP003 uses their legitimate credentials to access the access control settings.|\n", + "\n", + "Currently, at this point of the kill chain stage the TAP003 does not perform any simulations actions. Future versions of TAP003 aim to leverage more of the simulation to create and remove accounts at this stage." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "pprint(\"---Starting Client State----\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.ACCESS):\n", + " access_obs_impact, _, _, _, info = env.step(0)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Compare observation space before and after ACCESS stage.\n", + "display_obs_diffs(planning_obs_impact, access_obs_impact, env.game.step_counter)\n", + "\n", + "starting_host.software_manager.show()\n", + "print_agent_actions_except_do_nothing(tap003.config.ref)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | MANIPULATION\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|4|Manipulation| **Target Host(s)** HOST::SESSIONS:REMOTE |TAP003 exploits their insider knowledge/privilege to implement changes for sabotage.|\n", + "\n", + "In the ``MANIPULATION`` kill chain stage the TAP003 agent creates new and alters pre-existing user accounts based on user given settings. \n", + "\n", + "_For more information please refer to the later TAP003 customisation section of the notebook._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "access_obs_impact, _, _, _, _ = env.step(0)\n", + "pprint(f\"Agent action: {info['agent_actions']['attacker']}\")\n", + "\n", + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "pprint(\"---Target Client State----\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.MANIPULATION):\n", + " manipulation_obs_impact, _, _, _, info = env.step(0)\n", + "\n", + "\n", + "# Example host\n", + "rem_pub_rt_dr.user_manager.show()\n", + "# rem_pub_rt_dr.user_session_manager.show()\n", + "\n", + "# Compare observation space before and after MANIPULATION stage.\n", + "display_obs_diffs(access_obs_impact, manipulation_obs_impact, env.game.step_counter)\n", + "print_agent_actions_except_do_nothing(tap003.config.ref)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Kill Chain** | EXPLOITATION\n", + "\n", + "|Index| Action Stage| OBS Impact | Narrative |\n", + "|-----|-------------|------------|-----------|\n", + "|5|EXPLOITATION| **Target Host(s)** ROUTER_ACL_ADDRULE |TAP003 exploiting their insider knowledge/privilege to implement changes for sabotage|\n", + "\n", + "In the final attack stage the TAP003 agent insert a malicious ACL rule(s) which blocks specific traffic, therefore triggering negative rewards for the green agents.\n", + "\n", + "The table below shows the impacts:\n", + "\n", + "|Target Router | Impact |\n", + "|----------------------|--------|\n", + "|`ST_INTRA-PRV-RT-DR-1`| Blocks all `POSTGRES_SERVER` that arrives at the `ST_INTRA-PRV-RT-DR-1` router. This rule will prevent all ST_PROJ_* hosts from accessing the database (`ST_DATA-PRV-SRV-DB`).|\n", + "|`ST_INTRA-PRV-RT-CR`| Blocks all `HTTP` traffic that arrives at the`ST_INTRA-PRV-RT-CR` router. This rule will prevent all SOME_TECH hosts from accessing the webserver (`ST_DMZ-PUB-SRV-WEB`)|\n", + "|`REM-PUB-RT-DR`| Blocks all `DNS` traffic that arrives at the `REM-PUB-RT-DR` router. This rule prevents any remote site works from accessing the DNS Server (`ISP-PUB-SRV-DNS`).|\n", + "\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n", + "\n", + "_(Click to enlarge)_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "manipulation_obs_impact, _, _, _, _ = env.step(0)\n", + "pprint(f\"Agent action: {info['agent_actions']['attacker']}\")\n", + "\n", + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")\n", + "pprint(\"---Target Client State----\")\n", + "while(tap003.current_kill_chain_stage == InsiderKillChain.EXPLOIT):\n", + " exploit_obs_impact, _, _, _, info = env.step(0)\n", + " \n", + "st_intra_prv_rt_cr.acl.show()\n", + "st_intra_prv_rt_dr_1.acl.show()\n", + "rem_pub_rt_dr.acl.show()\n", + "\n", + "# Stepping once more to allow the malicious acl to appear in the simulation.\n", + "exploit_obs_impact, _, _, _, _ = env.step(0)\n", + "\n", + "# Compare observation space before and after MANIPULATION stage.\n", + "display_obs_diffs(manipulation_obs_impact, exploit_obs_impact, env.game.step_counter)\n", + "print_agent_actions_except_do_nothing(tap003.config.ref)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tap003.logger.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Demonstration of the ACL Rule blocking traffic\n", + "\n", + "As an example of the malicious ACL Rule affecting traffic across the network, attempting to query the database server (ST_DATA-PRV-SRV-DB) from any of the `ST_PROJECT_*` networks should fail because it is must route through the `ST_INTRA-PRV-RT-DR-1` router which TAP003 has configured to block all `POSTGRES_SERVER` traffic." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "affected_node = env.game.simulation.network.get_node_by_hostname(\"ST_PROJ-A-PRV-PC-1\")\n", + "database_server = env.game.simulation.network.get_node_by_hostname(\"ST_DATA-PRV-SRV-DB\")\n", + "\n", + "st_intra_prv_rt_dr_1 = env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-DR-1\")\n", + "\n", + "database_client: DatabaseClient = affected_node.software_manager.software.get(\"database-client\")\n", + "st_intra_prv_rt_dr_1.acl.show()\n", + "\n", + "# should be False\n", + "print(database_client.query(sql=\"SELECT\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, ACL rules will not stop ICMP traffic, allowing us to still ping the target nodes as shown below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# should be true\n", + "print(affected_node.ping(database_server.network_interface[1].ip_address))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, by default, TAP003 is configured to add two more ACLs on the UC7 network as shown in the table previously." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This ACL blocks all web_server traffic from within the main `SOME_TECH` office. Thus leading to every SOME_TECH host unable to access the web-server\n", + "st_intra_prv_rt_cr = env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_cr.acl.show()\n", + "\n", + "st_head_office_private_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"ST_HO-PRV-PC-1\")\n", + "st_head_office_private_pc_1_web_browser: WebBrowser = st_head_office_private_pc_1.software_manager.software[\"web-browser\"]\n", + "st_head_office_private_pc_1_web_browser.get_webpage(url=\"http://some_tech.com\")\n", + "st_head_office_private_pc_1_web_browser.sys_log.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This ACL blocks all DNS traffic from exiting the SOME_TECH remote site.\n", + "rem_pub_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(hostname=\"REM-PUB-PC-1\")\n", + "rem_pub_rt_dr = env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\")\n", + "dns_client: DNSClient = rem_pub_pc_1.software_manager.software[\"dns-client\"]\n", + "dns_client.dns_cache.clear()\n", + "dns_client.check_domain_exists(target_domain=\"some_tech.com\")\n", + "rem_pub_rt_dr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## **Kill Chain Configurations** | Overview:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following sections cover the TAP003's settings & configurations. \n", + "Each section contains code cells which directly demonstrate each option and it's effect on TAP003." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Threat Actor Profile (TAP) wide Agent Settings\n", + "\n", + " Threat Actor Profile Agent Settings \n", + "\n", + "
\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|start_step|The initial kill chain starting step.|Int|_Required_|\n", + "|frequency|The frequency of each kill chains starting steps.|Int|_Required_|\n", + "|variance| The timestep variance between frequency|Int|_Required_|\n", + "|repeat_kill_chain|Indicates whether the attack is repeated throughout the episode.|Bool|_Required_|\n", + "|repeat_kill_chain_stages|Indicates if the kill_chain stage should reset upon failure or retry.|Bool|_Required_|\n", + "|default_target_node|The Target Host|Str|_Required_|\n", + "|starting_nodes|A list of Potential Targets|List|_Optional_|\n", + "|kill_chain|_See the next notebook section_|Dict|_Required_||\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_Example kill chain setting configuration_\n", + "```yaml\n", + " start_step: 1\n", + " frequency: 3\n", + " variance: 0\n", + " repeat_kill_chain: false\n", + " repeat_kill_chain_stages: true\n", + " default_starting_node: \"ST_PROJ-A-PRV-PC-1\"\n", + " starting_nodes:\n", + " # starting_nodes: [\"ST_PROJ-A-PRV-PC-1\", \"ST_PROJ-B-PRV-PC-2\", \"ST_PROJ-C-PRV-PC-3\"]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Kill Chain Settings Overview\n", + "Currently, each kill chain stage has a probability of success option.\n", + "Additionally the manipulation kill stage offers an additional configuration of the malicious ACL.\n", + "\n", + " TAP003's Kill Chain Settings \n", + "\n", + "
\n", + "\n", + "Kill Chain Stage 1 - _PLANNING_\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Probability Of Success - The chance of successfully carrying out this stage in the kill_chain.|float|_Required_|\n", + "|starting_network_knowledge| User Account details | dict |_required_|\n", + "\n", + "Kill Chain Stage 2 - _ACCESS_\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Probability Of Success - The chance of successfully carrying out this stage in the kill_chain.|float|_Required_|\n", + "\n", + "Kill Chain Stage 3 - _MANIPULATION_\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Probability Of Success - The chance of successfully carrying out this stage in the kill_chain.|float|_Required_|\n", + "|account_changes| User Accounts hijacked - Must be the same targets as `malicious_acl` used in next kill chain stage | dict | _Required_|\n", + "\n", + "Kill Chain Stage 4 - _EXPLOIT_\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|malicious_acl|The configurable ACL that the TAP003 agent adds to the target node.|dict|_Required_|\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_An example of a kill chain setting configuration_ \n", + "```yaml\n", + " kill_chain:\n", + " PLANNING:\n", + " probability: 1\n", + " starting_network_knowledge:\n", + " credentials:\n", + " ST_PROJ-A-PRV-PC-1:\n", + " username: admin\n", + " password: admin\n", + " ST_PROJ-B-PRV-PC-2:\n", + " username: admin\n", + " password: admin\n", + " ST_PROJ-C-PRV-PC-3:\n", + " username: admin\n", + " password: admin\n", + " ST_INTRA-PRV-RT-DR-1:\n", + " ip_address: 192.168.230.1\n", + " username: admin\n", + " password: admin\n", + " ST_INTRA-PRV-RT-CR:\n", + " ip_address: 192.168.160.1\n", + " username: admin\n", + " password: admin\n", + " REM-PUB-RT-DR:\n", + " ip_address: 192.168.10.2\n", + " username: admin\n", + " password: admin\n", + " ACCESS:\n", + " probability: 1\n", + " MANIPULATION:\n", + " probability: 1\n", + " account_changes:\n", + " - host: ST_INTRA-PRV-RT-DR-1\n", + " ip_address: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1\n", + " action: change_password\n", + " username: admin\n", + " new_password: \"red_pass\"\n", + " - host: ST_INTRA-PRV-RT-CR\n", + " ip_address: 192.168.160.1 # ST_INTRA-PRV-RT-CR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " - host: REM-PUB-RT-DR\n", + " ip_address: 192.168.10.2 # REM-PUB-RT-DR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " EXPLOIT:\n", + " probability: 1\n", + " malicious_acls:\n", + " - target_router: ST_INTRA-PRV-RT-DR-1\n", + " ip_address: 192.168.230.1 \n", + " position: 1\n", + " permission: DENY\n", + " src_ip: ALL\n", + " src_wildcard_mask: 0.0.255.255\n", + " dst_ip: ALL\n", + " dst_wildcard_mask: 0.0.255.255\n", + " src_port: POSTGRES_SERVER\n", + " dst_port: POSTGRES_SERVER\n", + " protocol_name: TCP\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Attack Configurations** | Configuration Examples \n", + "The following notebook section demonstrates a few potential examples of different TAP003 configurations and their expected behaviour within an episode." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "tap003 = env.game.agents['attacker']\n", + "for key,value in tap003.config.agent_settings:\n", + " if key == 'kill_chain':\n", + " pass\n", + " else:\n", + " print(f\"{key} : {value}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Start Step, Frequency & Variance\n", + "\n", + "In this section, the code cells below demonstrate the following options:\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|start_step|The initial kill chain starting step.|Int|_Required_|\n", + "|frequency|The frequency of each kill chains starting steps.|Str|_Required_|\n", + "|variance|The timestep variance between frequency.|Str|_Required_|\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below alters the start_step, frequency and variance to 20, 10 and 5 respectively.\n", + "Additionally, the probability of the kill chain stages are set to guarantee success. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg[\"agents\"][33][\"agent_settings\"][\"flatten_obs\"] = False\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['agents'][32]['agent_settings']['start_step'] = 20\n", + " cfg['agents'][32]['agent_settings']['frequency'] = 10\n", + " cfg['agents'][32]['agent_settings']['variance'] = 5\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['MANIPULATION']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['ACCESS']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['probability'] = 1\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The cell below demonstrates how the frequency & variance options affect TAP003's next execution step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "tap003 = env.game.agents['attacker']\n", + "print(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | repeat_kill_chain & repeat_kill_chain_stages\n", + "\n", + "This section demonstrate how the different repeat options may affect a 100 step length episode.\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|repeat_kill_chain|Indicates whether the attack is repeated throughout the episode.|Str|_Required_|\n", + "|repeat_kill_chain_stages|Indicates if the kill_chain stage should reset upon failure or retry.|Str|_Required_|\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = False\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = True\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = True\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "while(env.game.step_counter != 100):\n", + " env.step(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | Starting Node\n", + "\n", + "The code cells below demonstrate how to configure TAP003's different options for selecting starting hosts.\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|default_starting_node|The default starting host.|Str|_Required_|\n", + "|starting_nodes|A list of potential targets|List|_Optional_|\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg[\"agents\"][32][\"agent_settings\"][\"default_starting_node\"] = \"ST_PROJ-A-PRV-PC-1\"\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "tap003 = env.game.agents[\"attacker\"]\n", + "print(f\"TA003's Selected Starting Host: {tap003.starting_node}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below demonstrates how starting node lists overrule the default start node options:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg[\"agents\"][32][\"agent_settings\"][\"default_starting_node\"] = \"ST_PROJ-A-PRV-PC-1\"\n", + " cfg[\"agents\"][32][\"agent_settings\"][\"starting_nodes\"] = [\"ST_PROJ-A-PRV-PC-1\",\"ST_PROJ-B-PRV-PC-2\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "env = PrimaiteGymEnv(env_config = cfg)\n", + "tap003 = env.game.agents[\"attacker\"]\n", + "print(f\"TA003's Selected Starting Host: {tap003.starting_node}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | probability\n", + "\n", + "The code cells below configure each kill chain's probability of success. \n", + "The cells also demonstrate how TAP003 handles failure within an episode.\n", + "\n", + "|Option Field|Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability | The chance of successfully carrying out this stage in the kill chain.|float|_Required_|\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below directly sets each probability of success to 0.25.
\n", + "Additionally the repeat kill chain and kill chain stage configuration options are set to false." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = False\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['probability'] = 0.25\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['ACCESS']['probability'] = 0.25\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['probability'] = 0.25\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "while(env.game.step_counter != 100):\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below performs the same episode but with the repeat_kill_chain and repeat_kill_chain_stages to true." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = True\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain_stages'] = True\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['MANIPULATION']['probability'] = 0.25\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['ACCESS']['probability'] = 0.25\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['probability'] = 0.25\n", + "env = PrimaiteGymEnv(env_config = cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "while(env.game.step_counter != 100):\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "pprint(\"---TAP003's Current State---\")\n", + "print(f\"Current Environment Step: {env.game.step_counter}\")\n", + "print(f\"Current Kill Chain Stage: {tap003.current_kill_chain_stage.name}\")\n", + "print(f\"Next Execution Step: {tap003.next_execution_timestep}\")\n", + "print(f\"Next Kill Chain Stage {tap003.next_kill_chain_stage.name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | ACCESS - Starting Network Knowledge\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|probability |Action Probability - The chance of successfully carrying out this stage in the kill_chain.|str|_Required_|\n", + "|starting_network_knowledge|TAP003's starting knowledge. Used to login into the target accounts in the ``MANIPULATION`` stage.|dict|_Required_|" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``starting_network_knowledge``\n", + "\n", + "***\n", + "\n", + "The ``starting_network_knowledge`` options uses the following schema:\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|credentials|The credential knowledge does TAP003 starts the episode with.|dict|_Required_|\n", + "\n", + "```yaml\n", + " kill_chain:\n", + " PLANNING:\n", + " probability: 1\n", + " starting_network_knowledge:\n", + " credentials:\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``credentials``\n", + "\n", + "***\n", + "\n", + "The ``credentials`` options uses the following schema:\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|ip_address |The IP Address of the target host.|str|_Required_|\n", + "|username |A username of a valid user account. Defaults to ``Admin``.|str|_Optional_|\n", + "|password |The corresponding valid password of the given username. Defaults to ``Admin``.|str|_Optional_|\n", + "\n", + "These credentials will are used in the ``MANIPULATION`` kill chain stage to login into the target accounts. Therefore the ``credentials`` options needs contain the same hosts that the the ``ACCOUNT_CHANGES`` option\n", + "\n", + "```yaml\n", + "\n", + " kill_chain:\n", + " PLANNING:\n", + " probability: 1\n", + " starting_network_knowledge:\n", + " credentials:\n", + " ST_PROJ-A-PRV-PC-1:\n", + " username: admin\n", + " password: admin\n", + " ST_PROJ-B-PRV-PC-2:\n", + " username: admin\n", + " password: admin\n", + " ST_PROJ-C-PRV-PC-3:\n", + " username: admin\n", + " password: admin\n", + " ST_INTRA-PRV-RT-DR-1:\n", + " ip_address: 192.168.230.1\n", + " username: admin\n", + " password: admin\n", + " ST_INTRA-PRV-RT-CR:\n", + " ip_address: 192.168.160.1\n", + " username: admin\n", + " password: admin\n", + " REM-PUB-RT-DR:\n", + " ip_address: 192.168.10.2\n", + " username: admin\n", + " password: admin\n", + "\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "tap003 = env.game.agents['attacker']\n", + "# This while loop will run TAP003 until it hits TAP003 reaches ``ACCESS`` (Therefore passing PLANNING)\n", + "while(tap003.current_kill_chain_stage != InsiderKillChain.ACCESS):\n", + " env.step(0)\n", + "for account in tap003.network_knowledge['credentials']:\n", + " print(f\"----{account}---\")\n", + " for key in tap003.network_knowledge['credentials'][account]:\n", + " pprint(f\"{key}: {tap003.network_knowledge['credentials'][account][key]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | Manipulation - Account Changes\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Action Probability - The chance of successfully carrying out this stage in the kill_chain.|str|_Required_|\n", + "|account_changes|List of dictionaries containing the target hostnames, ip and account configuration changes.|list[dict]|_Required_|\n", + "\n", + "```yaml\n", + " kill_chain:\n", + " MANIPULATION:\n", + " probability: 1\n", + " account_changes:\n", + " - host: ST_INTRA-PRV-RT-DR-1\n", + " ip_address: 192.168.230.1 # ST_INTRA-PRV-RT-DR-1\n", + " action: change_password\n", + " username: admin\n", + " new_password: \"red_pass\"\n", + " - host: ST_INTRA-PRV-RT-CR\n", + " ip_address: 192.168.160.1 # ST_INTRA-PRV-RT-CR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + " - host: REM-PUB-RT-DR\n", + " ip_address: 192.168.10.2 # REM-PUB-RT-DR\n", + " action: change_password\n", + " username: \"admin\"\n", + " new_password: \"red_pass\"\n", + "\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``account_changes``\n", + "\n", + "***\n", + "\n", + "The ``account_changes`` options uses the following schema:\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|host|The hostname of the target host.|str|_Required_|\n", + "|ip_address|The IP Address of the target host.|str|_Required_|\n", + "|action|The user account manager action. Currently only ``change_password`` is available.|str|_Required_|\n", + "|username|The username of the account that TAP003 will target.|str|_Required_|\n", + "|new_password|The new password that TAP003 will set.|str|_Required_|\n", + "\n", + "_For further information around the simulation implementation of user sessions please consult the ``user_session_manager`` primAITE API documentation page within PrimAITE's user guide documentation._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | Manipulation - Sandbox \n", + "\n", + "The code cells below can be used as a sandbox to trial out different configuration options and their impact on the simulation. Additionally, if users wish to alter an account other than admin on this sandbox they can alter the ``new_users`` dictionary in the code cell below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# New ST_PROJ-B-PRV-PC-2 user account\n", + "user_username = \"example_user_1\"\n", + "user_password = \"example_pass_1\"\n", + "user_admin = \"False\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "new_users = {\"username\": user_username, \"password\": user_password, \"is_admin\": user_admin }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Changing the password\n", + "tap003_new_password = \"tap003_password\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "account_changes = [{\"host\":\"ST_PROJ-A-PRV-PC-2\", \"ip_address\": \"192.168.230.2\", \"username\": user_username, \"old_password\": user_password, \"new_password\": tap003_new_password}]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we update the UC7 config and implement the new user accounts and run through an episode." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + " cfg['simulation']['network']['nodes'][31].update({'users':[]})\n", + " cfg['simulation']['network']['nodes'][31]['users'].append(new_users)\n", + " cfg['agents'][32]['agent_settings']['start_step'] = 1\n", + " cfg['agents'][32]['agent_settings']['frequency'] = 3\n", + " cfg['agents'][32]['agent_settings']['variance'] = 0\n", + " starting_creds = {\"username\": \"admin\",\"password\":\"admin\", \"ip_address\":\"192.168.230.2\"} # Adding \"ST_PROJ-A-PRV-PC-2\" to TAP003's starting_network_knowledge\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['starting_network_knowledge']['credentials'].update({\"ST_PROJ-A-PRV-PC-2\": starting_creds})\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['MANIPULATION']['account_changes'] = account_changes\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "env.reset()\n", + "while(env.game.step_counter != 90): # 20 Red Actions (frequency of 3)\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "target_host = env.game.simulation.network.get_node_by_hostname(\"ST_PROJ-A-PRV-PC-2\")\n", + "target_host.user_manager.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | Exploit - Malicious ACL\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|------------|-------|-------------|-----------------|\n", + "|probability|Action Probability - The chance of successfully carrying out this stage in the kill_chain.|str|_Required_|\n", + "|malicious_acls|The configurable ACL that the TAP003 agent adds to the target node.|dict|_Required_|\n", + "\n", + "The malicious ACL is configured identically to the other ACLs. except from the target router/firewall. \n", + "This option is set to the TAP003's configured target host automatically.\n", + "\n", + "TAP003 intends to leverage these ACL's for malicious purposes. The default configuration is to deny all traffic from and towards the 0.0.0.255 subnet. \n", + "The configurability of the malicious ACL will allow for future releases of TAP type agents itself to perform more complex attacks. Currently, the TAP003 aims to block green agent traffic." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``malicious_acl``\n", + "\n", + "***\n", + "\n", + "The ``malicious_acl`` options uses the following schema:\n", + "\n", + "|Option Field |Meaning|Expected Type|Required/Optional|\n", + "|-------------|-------|-------------|-----------------|\n", + "|target_router|The hostname of target router.|str|_Required_|\n", + "|ip_address|The ip_address of target router.|str|_Required_|\n", + "|position|The position that the malicious ACL is placed.|str|_Required_|\n", + "|permission|The implicit action. Should the ACL ``DENY`` or ``PERMIT``.|str|_Required_|\n", + "|src_ip|The source IP address to be targeted by the ``ACL``.|str|_Required_|\n", + "|src_wildcard_mask|The wildcard mask for the source ip address.|str|_Required_|\n", + "|dst_ip|The destination IP address to be targeted by the ``ACL``.|str|_Required_|\n", + "|dst_wildcard_mask|The wildcard mask for the destination ip address.|str|_Required_|\n", + "|src_port|The source port to be targeted by the ``ACL``.|str|_Required_|\n", + "|dst_port|The destination port to be targeted by the ``ACL``.|str|_Required_|\n", + "|protocol_name|The transport layer protocol_name to be targeted.|str|_Required_|\n", + "\n", + "_For further information please consult the simulation ACL documentation within PrimAITE's Sphinx documentation._" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below demonstrates the default ACL's added by TAP003:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['start_step'] = 1\n", + " cfg['agents'][32]['agent_settings']['frequency'] = 3 # Install action takes multiple timesteps.\n", + " cfg['agents'][32]['agent_settings']['variance'] = 0\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['MANIPULATION']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['ACCESS']['probability'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['PLANNING']['probability'] = 1\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "while(env.game.step_counter != 70):\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "for key,value in tap003.config.agent_settings:\n", + " if key != 'kill_chain':\n", + " pass\n", + " else:\n", + " for kill_chain_setting_key in value:\n", + " if kill_chain_setting_key == 'EXPLOIT':\n", + " pprint(value[kill_chain_setting_key]['malicious_acls'][0])\n", + " pprint(value[kill_chain_setting_key]['malicious_acls'][1])\n", + " pprint(value[kill_chain_setting_key]['malicious_acls'][2])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_dr_1 = env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-DR-1\")\n", + "st_intra_prv_rt_dr_1.acl.show()\n", + "\n", + "rem_pub_rt_dr = env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\")\n", + "rem_pub_rt_dr.acl.show()\n", + "\n", + "st_intra_prv_rt_cr = env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_cr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unlike the blue agent, TAP003 does not need to use it's action space options for indexing different options, meaning that ACL's are a lot easier to configure.\n", + "\n", + "The sandbox below can be used to try out different configuration options and their impact on the simulation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### **Attack Configurations** | kill_chain_settings | Exploit - Sandbox " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "metadata": {} + }, + "outputs": [], + "source": [ + "# please ensure that the source/dest ip_addresses are within the blue agent's observation space ip_list option.\n", + "with open(_EXAMPLE_CFG/\"uc7_config_tap003.yaml\", mode=\"r\") as f:\n", + " cfg = yaml.safe_load(f)\n", + " cfg['agents'][32]['agent_settings']['repeat_kill_chain'] = False\n", + " cfg['agents'][32]['agent_settings']['start_step'] = 1\n", + " cfg['agents'][32]['agent_settings']['frequency'] = 3\n", + " cfg['agents'][32]['agent_settings']['variance'] = 0\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['position'] = 1\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['permission'] = \"DENY\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['src_ip'] = \"ALL\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['src_wildcard'] = '0.0.255.255'\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['dst_ip'] = 'ALL'\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['dst_wildcard'] = \"0.0.255.255\"\n", + " # Please refer to the ``PORT`` class in the primAITE API for more information around the different supported ports\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['src_port'] = 'POSTGRES_SERVER'\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['dst_port'] = \"POSTGRES_SERVER\"\n", + " cfg['agents'][32]['agent_settings']['kill_chain']['EXPLOIT']['malicious_acls'][0]['protocol_name'] = 'ALL'\n", + "env = PrimaiteGymEnv(env_config = cfg)\n", + "while(env.game.step_counter != 50):\n", + " env.step(0)\n", + "tap003 = env.game.agents['attacker']\n", + "target_host = env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-DR-1\")\n", + "target_host.acl.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-Training.ipynb b/src/primaite/notebooks/UC7-Training.ipynb new file mode 100644 index 00000000..ddaa6844 --- /dev/null +++ b/src/primaite/notebooks/UC7-Training.ipynb @@ -0,0 +1,147 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "# Training an SB3 Agent\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n", + "\n", + "This notebook will demonstrate how to use primaite to create and train a PPO agent, using a pre-defined configuration file." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### First, we import the inital packages and read in our configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite import PRIMAITE_PATHS\n", + "from prettytable import PrettyTable\n", + "from deepdiff.diff import DeepDiff\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "\n", + "scenario_path = PRIMAITE_PATHS.user_config_path / \"example_config/uc7_config.yaml\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gym = PrimaiteGymEnv(env_config=scenario_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stable_baselines3 import PPO\n", + "\n", + "# EPISODE_LEN = 128\n", + "EPISODE_LEN = 128\n", + "NUM_EPISODES = 10\n", + "NO_STEPS = EPISODE_LEN * NUM_EPISODES\n", + "BATCH_SIZE = 32\n", + "LEARNING_RATE = 3e-4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = PPO('MlpPolicy', gym, learning_rate=LEARNING_RATE, n_steps=NO_STEPS, batch_size=BATCH_SIZE, verbose=0, tensorboard_log=\"./PPO_UC7/\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.learn(total_timesteps=NO_STEPS)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.save(\"PrimAITE-PPO-UC7-example-agent\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "eval_model = PPO(\"MlpPolicy\", gym)\n", + "eval_model = PPO.load(\"PrimAITE-PPO-UC7-example-agent\", gym)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stable_baselines3.common.evaluation import evaluate_policy\n", + "\n", + "evaluate_policy(eval_model, gym, n_eval_episodes=1)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-attack-variants.ipynb b/src/primaite/notebooks/UC7-attack-variants.ipynb new file mode 100644 index 00000000..14742796 --- /dev/null +++ b/src/primaite/notebooks/UC7-attack-variants.ipynb @@ -0,0 +1,586 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# UC7 with Attack Variability\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "This notebook demonstrates the PrimAITE environment with the UC7 network laydown and multiple attack personas. The first attack persona is TAP001 which performs a ransomware attack against the database. The other one is TAP003 which is able to maliciously add ACL rules that block green pattern of life.\n", + "\n", + "The environment switches between these two attacks on a pre-defined schedule which is defined in the schedule.yaml file of the scenario folder." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup and Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite import PRIMAITE_PATHS\n", + "from prettytable import PrettyTable\n", + "from deepdiff.diff import DeepDiff\n", + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.system.services.dns.dns_server import DNSServer\n", + "from primaite.simulator.system.software import SoftwareHealthState\n", + "from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus\n", + "from primaite.simulator.network.hardware.nodes.network.switch import Switch\n", + "from primaite.simulator.system.applications.web_browser import WebBrowser\n", + "from primaite.simulator.network.container import Network\n", + "from primaite.simulator.system.services.service import ServiceOperatingState\n", + "from primaite.simulator.network.hardware.node_operating_state import NodeOperatingState\n", + "from primaite.simulator.system.services.database.database_service import DatabaseService\n", + "from primaite.simulator.system.applications.database_client import DatabaseClient\n", + "from primaite.simulator.network.hardware.nodes.network.firewall import Firewall\n", + "from primaite.game.game import PrimaiteGame\n", + "from primaite.simulator.sim_container import Simulation\n", + "from primaite.config.load import load, _EXAMPLE_CFG\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "\n", + "scenario_path = PRIMAITE_PATHS.user_config_path / \"example_config/uc7_multiple_attack_variants\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = PrimaiteGymEnv(env_config=scenario_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Schedule\n", + "\n", + "Let's print the schedule so that we can see which attack we can expect on each episode.\n", + "\n", + "On episodes 0-4, the TAP001 agent will be used, and on episodes 5-9, the TAP003 agent will be used. Then, the environment will alternate between the two. Furthermore, the TAP001 agent will alternate between starting at `ST_PROJ-A-PRV-PC-1`, `ST_PROJ-B-PRV-PC-2`, `ST_PROJ-C-PRV-PC-3`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(scenario_path / \"schedule.yaml\",'r') as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TAP001 attack\n", + "\n", + "Let's first demonstrate the TAP001 attack. We will let the environment run for 30 steps and print out the red agent's actions.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#utils\n", + "def run_green_and_red_pol(num_steps):\n", + " for i in range(num_steps): # perform steps\n", + " env.step(0)\n", + "\n", + "def print_agent_actions_except_do_nothing(agent_name):\n", + " \"\"\"Get the agent's action history, filter out `do-nothing` actions, print relevant data in a table.\"\"\"\n", + " table = PrettyTable()\n", + " table.field_names = [\"Step\", \"Action\", \"Node\", \"Application\", \"Target IP\", \"Response\"]\n", + " print(f\"Episode: {env.episode_counter}, Actions for '{agent_name}':\")\n", + " for item in env.game.agents[agent_name].history:\n", + " if item.action == \"do-nothing\":\n", + " continue\n", + "\n", + " node, application, target_ip = \"N/A\", \"N/A\", \"N/A\",\n", + "\n", + " if item.action.startswith(\"node-nmap\"):\n", + " node = item.parameters['source_node']\n", + " application = \"nmap\"\n", + " target_ip = str(item.parameters['target_ip_address'])\n", + " target_ip = (target_ip[:25]+'...') if len(target_ip)>25 else target_ip # truncate long string\n", + "\n", + " elif item.action == \"router-acl-add-rule\":\n", + " node = item.parameters.get(\"router_name\")\n", + " elif item.action == \"node-send-remote-command\":\n", + " node = item.parameters.get(\"node_name\")\n", + " target_ip = item.parameters.get(\"remote_ip\")\n", + " application = item.parameters.get(\"command\")\n", + " elif item.action == \"node-session-remote-login\":\n", + " node = item.parameters.get(\"node_name\")\n", + " target_ip = item.parameters.get(\"remote_ip\")\n", + " application = \"user-manager\"\n", + " elif item.action.startswith(\"c2-server\"):\n", + " application = \"c2-server\"\n", + " node = item.parameters.get('node_name')\n", + " elif item.action == \"configure-c2-beacon\":\n", + " application = \"c2-beacon\"\n", + " node = item.parameters.get('node_name')\n", + "\n", + " else:\n", + " if (node_id := item.parameters.get('node_id')) is not None:\n", + " node = env.game.agents[agent_name].action_manager.node_names[node_id]\n", + " if (application_id := item.parameters.get('application_id')) is not None:\n", + " application = env.game.agents[agent_name].action_manager.application_names[node_id][application_id]\n", + " if (application_name := item.parameters.get('application_name')) is not None:\n", + " application = application_name\n", + "\n", + " table.add_row([item.timestep, item.action, node, application, target_ip, item.response.status])\n", + "\n", + " print(table)\n", + " print(\"(Any do-nothing actions are omitted)\")\n", + "\n", + "def finish_episode_and_print_reward():\n", + " while env.game.step_counter < 128:\n", + " env.step(0)\n", + " print(f\"Total reward this episode: {env.agent.reward_function.total_reward:2f}\")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "run_green_and_red_pol(110)\n", + "print_agent_actions_except_do_nothing(\"attacker\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_data_prv_srv_db: Server = env.game.simulation.network.get_node_by_hostname(\"ST_DATA-PRV-SRV-DB\")\n", + "st_data_prv_srv_db.file_system.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "finish_episode_and_print_reward()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TAP001 Prevention" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The blue agent should be able to prevent the ransomware attack by blocking the red agent's access to the database. Let's run the environment until the observation space shows symptoms of the attack starting.\n", + "\n", + "Because we are in episode index 1, the red agent will use `ST-PROJ-A-PRV-PC-1` to start the attack. On step 25, the red agent installs `RansomwareScript`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "obs, reward, term, trunc, info = env.step(0)\n", + "for i in range(25): # we know that the ransomware install happens at step 25\n", + " old = obs\n", + " obs, reward, term, trunc, info = env.step(0)\n", + " new = obs\n", + "\n", + "diff = DeepDiff(old,new)\n", + "print(f\"Step {env.game.step_counter}\") # it's step 26 now because the step counter is incremented after the step\n", + "for d,v in diff.get('values_changed', {}).items():\n", + " print(f\"{d}: {v['old_value']} -> {v['new_value']}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that on HOST0, application index 1 has gone from `operating_status` 0 to 3, meaning there wasn't an application before, but now there is an application in the `INSTALLING` state. The blue agent should be able to detect this and block the red agent's access to the database. Action 43 will block `ST-PROJ-A-PRV-PC-1` from sending POSTGRES traffic to the DB server.\n", + "\n", + "If this were a different episode, it could have been `ST-PROJ-B-PRV-PC-2` or `ST-PROJ-C-PRV-PC-3` that are affected, and a different defensive action would be required." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(43)\n", + "env.step(45)\n", + "env.step(47)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr: Router = env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_cr.acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "finish_episode_and_print_reward()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr.acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now TAP001 is unable to locate the database!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print_agent_actions_except_do_nothing(\"attacker\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TAP003 attack\n", + "\n", + "Let's skip until episode 5 and demonstrate the TAP003 attack. We will let the environment run and print out the red agent's actions.\n", + "\n", + "By default, TAP003 will add the following rules:\n", + "\n", + "|Target Router | Impact |\n", + "|----------------------|--------|\n", + "|`ST_INTRA-PRV-RT-DR-1`| Blocks all `POSTGRES_SERVER` that arrives at the `ST_INTRA-PRV-RT-DR-1` router. This rule will prevent all ST_PROJ_* hosts from accessing the database (`ST_DATA-PRV-SRV-DB`).|\n", + "|`ST_INTRA-PRV-RT-CR`| Blocks all `HTTP` traffic that arrives at the`ST_INTRA-PRV-RT-CR` router. This rule will prevent all SOME_TECH hosts from accessing the webserver (`ST-DMZ-PUB-SRV-WEB`)|\n", + "|`REM-PUB-RT-DR`| Blocks all `DNS` traffic that arrives at the `REM-PUB-RT-DR` router. This rule prevents any remote site works from accessing the DNS Server (`ISP-PUB-SRV-DNS`).|" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "while env.episode_counter < 5:\n", + " env.reset()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "run_green_and_red_pol(128)\n", + "print_agent_actions_except_do_nothing(\"attacker\")\n", + "obs, reward, term, trunc, info = env.step(0); # one more step so we can capture the value of `obs`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The agent selected to add ACL rules that will prevent green pattern of life by blocking a variety of different traffic. This has a negative impact on reward. Let's view the ACL list on the affected router." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-DR-1\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-CR\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\").acl.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that at indices 1-5, there are ACL rules that block all traffic. The blue agent can see this rule in the `ROUTERS` part of the observation space.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs['NODES']['ROUTER0']['ACL'][1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs['NODES']['ROUTER1']['ACL'][1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obs['NODES']['ROUTER2']['ACL'][1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preventing TAP003 attack\n", + "\n", + "The blue agent can prevent the red agent from adding ACL rules. TAP003 relies on connecting to the router via SSH, and sending remote ACL_ADDRULE requests. The blue agent can prevent this by pre-emptively changing the admin password on the affected routers or by blocking SSH traffic between the red agent's starting node and the target routers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "obs, reward, term, trunc, info = env.step(0)\n", + "old = obs\n", + "for i in range(128): \n", + " obs, reward, term, trunc, info = env.step(0)\n", + " new = obs\n", + "\n", + "diff = DeepDiff(old,new)\n", + "print(f\"Step {env.game.step_counter}\") # it's the next step now because the step counter is incremented after the step\n", + "for d,v in diff.get('values_changed', {}).items():\n", + " print(f\"{d}: {v['old_value']} -> {v['new_value']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By printing the reward of each individual agent, we will see what green agents are affected the most. Of course, these green rewards count towards the blue reward so ultimately the blue agent should learn to remove the ACL rule." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "finish_episode_and_print_reward()\n", + "\n", + "for ag in env.game.agents.values():\n", + " print(ag.config.ref, ag.reward_function.total_reward)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The most effective option that the blue agent has against TAP003 is to prevent the red agent from ever adding the ACLs in the first place through blocking the SSH connection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "env.step(51) # SSH Blocking ACL on ST-INRA-PRV-RT-R1\n", + "finish_episode_and_print_reward()\n", + "\n", + "for ag in env.game.agents.values():\n", + " print(ag.config.ref, ag.reward_function.total_reward)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, another option the blue agent can take is to change the passwords of the different target routers that TAP003 will attack through the `NODE_ACCOUNTS_CHANGE_PASSWORD` action." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "env.step(50) # NODE_ACCOUNTS_CHANGE_PASSWORD | ST_INTRA-prv-rt-cr\n", + "env.step(52) # NODE_ACCOUNTS_CHANGE_PASSWORD | ST_INTRA-prv-rt-dr-1\n", + "env.step(54) # NODE_ACCOUNTS_CHANGE_PASSWORD | rem-pub-rt-dr\n", + "finish_episode_and_print_reward()\n", + "\n", + "for ag in env.game.agents.values():\n", + " print(ag.config.ref, ag.reward_function.total_reward)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, the blue agent can remedy the impacts of TAP003 through removing the malicious ACLs that TAP003 adds." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.reset()\n", + "\n", + "# Allow TAP003 to add it's malicious rules\n", + "for _ in range(45):\n", + " env.step(0)\n", + "\n", + "env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-CR\").acl.show()\n", + "env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-DR-1\").acl.show()\n", + "env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\").acl.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(44) # ROUTER_ACL_REMOVERULE | ST_INTRA-prv-rt-cr\n", + "env.step(53) # ROUTER_ACL_REMOVERULE | ST_INTRA-prv-rt-dr-1\n", + "env.step(55) # ROUTER_ACL_REMOVERULE | rem-pub-rt-dr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-CR\").acl.show()\n", + "env.game.simulation.network.get_node_by_hostname(\"ST_INTRA-PRV-RT-DR-1\").acl.show()\n", + "env.game.simulation.network.get_node_by_hostname(\"REM-PUB-RT-DR\").acl.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "finish_episode_and_print_reward()\n", + "\n", + "for ag in env.game.agents.values():\n", + " print(ag.config.ref, ag.reward_function.total_reward)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/UC7-network_connectivity.ipynb b/src/primaite/notebooks/UC7-network_connectivity.ipynb new file mode 100644 index 00000000..8aef5465 --- /dev/null +++ b/src/primaite/notebooks/UC7-network_connectivity.ipynb @@ -0,0 +1,1126 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# UC7 Demonstration\n", + "\n", + "© Crown-owned copyright 2025, Defence Science and Technology Laboratory UK\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Network Description\n", + "\n", + "

\n", + " \n", + " \"Image\"\n", + " \n", + " \n", + "

\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!primaite setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from primaite.session.environment import PrimaiteGymEnv\n", + "from primaite.simulator.network.hardware.nodes.host.computer import Computer\n", + "from primaite.simulator.network.hardware.nodes.host.server import Server\n", + "from primaite.simulator.network.hardware.nodes.network.router import Router\n", + "from primaite.simulator.network.hardware.nodes.network.switch import Switch\n", + "from primaite.simulator.network.container import Network\n", + "from primaite.simulator.network.hardware.nodes.network.firewall import Firewall\n", + "from primaite.game.game import PrimaiteGame\n", + "from primaite.simulator.sim_container import Simulation\n", + "import yaml\n", + "from pprint import pprint\n", + "from primaite.config.load import load, _EXAMPLE_CFG" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open(file=_EXAMPLE_CFG/\"uc7_config.yaml\", mode=\"r\") as uc7_config:\n", + " cfg = yaml.safe_load(uc7_config)\n", + " cfg['io_settings']['save_sys_logs'] = True # Saving syslogs\n", + " cfg['io_settings']['save_agent_logs'] = True # Save agent logs\n", + "env = PrimaiteGymEnv(env_config=cfg)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "uc7_network = env.game.simulation.network\n", + "uc7_network.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Home Office Subnet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_pc_1: Computer = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-PC-1\")\n", + "home_pub_pc_2: Computer = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-PC-2\")\n", + "home_pub_pc_srv: Server = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-SRV\")\n", + "home_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname(\"HOME-PUB-RT-DR\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME PC 1 --> HOME PC 2\n", + "home_pub_pc_1.ping(home_pub_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME PC 2 --> HOME SERVER\n", + "home_pub_pc_2.ping(home_pub_pc_srv.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME SERVER --> HOME ROUTER\n", + "home_pub_pc_srv.ping(home_pub_rt_dr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Internet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isp_pub_rt_br: Router = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-RT-BR\")\n", + "isp_pub_srv_dns: Server = env.game.simulation.network.get_node_by_hostname(\"ISP-PUB-SRV-DNS\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_pc_1.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME ROUTER --> INTERNET ROUTER\n", + "home_pub_rt_dr.ping(isp_pub_rt_br.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "home_pub_rt_dr.route_table.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME ROUTER --> INTERNET ISP\n", + "home_pub_rt_dr.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# INTERNET ROUTER --> INTERNET DNS\n", + "isp_pub_rt_br.ping(target_ip_address=isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HOME ROUTER --> INTERNET DNS\n", + "home_pub_rt_dr.ping(target_ip_address=isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# PC 1 --> INTERNET DNS\n", + "\n", + "home_pub_pc_1.ping(target_ip_address=isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isp_pub_rt_br.show_nic()\n", + "isp_pub_rt_br.ping(\"192.168.1.2\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Remote Site Subnet" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "network: Network = env.game.simulation.network\n", + "\n", + "\n", + "rem_pub_fw: Firewall = network.get_node_by_hostname(hostname=\"REM-PUB-FW\")\n", + "rem_pub_rt_dr: Router = network.get_node_by_hostname(hostname=\"REM-PUB-RT-DR\")\n", + "rem_pub_sw_as: Switch = network.get_node_by_hostname(hostname=\"REM-PUB-SW-AS\")\n", + "rem_pub_pc_1: Computer = network.get_node_by_hostname(hostname=\"REM-PUB-PC-1\")\n", + "rem_pub_pc_2: Computer = network.get_node_by_hostname(hostname=\"REM-PUB-PC-2\")\n", + "rem_pub_srv: Computer = network.get_node_by_hostname(hostname=\"REM-PUB-SRV\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote PC 1 --> Remote PC_2\n", + "\n", + "rem_pub_pc_1.ping(rem_pub_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote PC 2 --> Remote Server\n", + "\n", + "rem_pub_pc_2.ping(rem_pub_srv.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Server --> Remote Site Router\n", + "\n", + "rem_pub_srv.ping(rem_pub_rt_dr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Site Router --> Remote Pub Firewall (Internal)\n", + "\n", + "rem_pub_rt_dr.ping(rem_pub_fw.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Site Router --> Remote Pub Firewall (External)\n", + "\n", + "rem_pub_rt_dr.ping(rem_pub_fw.network_interface[2].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Pub Firewall (external) --> Public Internet\n", + "\n", + "rem_pub_fw.ping(isp_pub_rt_br.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Site PC 1 --> Public DNS\n", + "\n", + "rem_pub_pc_1.ping(target_ip_address=isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remote Site PC 1 --> Home Office PC 1\n", + "\n", + "rem_pub_pc_1.ping(target_ip_address=home_pub_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | SOME_TECH_DMZ" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_pub_fw: Firewall = network.get_node_by_hostname(hostname=\"ST_PUB-FW\")\n", + "st_dmz_pub_srv_web: Server = network.get_node_by_hostname(hostname=\"ST_DMZ-PUB-SRV-WEB\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Firewall --> Some Tech DMZ public web server\n", + "\n", + "st_pub_fw.ping(st_dmz_pub_srv_web.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Firewall --> Public DNS Server\n", + "\n", + "st_pub_fw.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech DMZ public web serv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Firewall --> Internet Router (Port 4)\n", + "\n", + "st_pub_fw.ping(isp_pub_rt_br.network_interface[4].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | SOME_TECH_INTRANET" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr: Router = network.get_node_by_hostname(hostname=\"ST_INTRA-PRV-RT-CR\")\n", + "st_intra_prv_rt_dr_1: Router = network.get_node_by_hostname(hostname=\"ST_INTRA-PRV-RT-DR-1\")\n", + "st_intra_prv_rt_dr_2: Router = network.get_node_by_hostname(hostname=\"ST_INTRA-PRV-RT-DR-2\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Intranet Private Router CR --> Some Tech Public Firewall\n", + "\n", + "st_intra_prv_rt_cr.ping(st_pub_fw.network_interface[3].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some Tech Intranet Router CR\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_intra_prv_rt_cr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 2 --> Some Tech Intranet Router CR\n", + "\n", + "st_intra_prv_rt_dr_2.ping(st_intra_prv_rt_cr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some tech intranet router DR 2\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_intra_prv_rt_dr_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some Tech Firewall (Internal Port)\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_pub_fw.network_interface[3].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some Tech Firewall (DMZ Port)\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_pub_fw.network_interface[2].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Some Tech Firewall (External Port)\n", + "\n", + "st_intra_prv_rt_dr_1.ping(st_pub_fw.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Intranet Router DR 1 --> Some Tech DMZ web-server\n", + "\n", + "st_intra_prv_rt_dr_1.ping(target_ip_address=st_dmz_pub_srv_web.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Public Internet\n", + "\n", + "st_intra_prv_rt_dr_1.ping(isp_pub_rt_br.network_interface[3].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some tech intranet router DR 1 --> Public DNS \n", + "\n", + "st_intra_prv_rt_dr_1.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Intranet Router DR 1 --> Home Office PC 1\n", + "\n", + "st_intra_prv_rt_dr_1.ping(target_ip_address=home_pub_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Some Tech Intranet Router DR 1 --> Remote Site PC 1\n", + "\n", + "st_intra_prv_rt_dr_1.ping(target_ip_address=rem_pub_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Head Office" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_head_office_private_pc_1: Computer = network.get_node_by_hostname(\"ST_HO-PRV-PC-1\")\n", + "st_head_office_private_pc_2: Computer = network.get_node_by_hostname(\"ST_HO-PRV-PC-2\")\n", + "st_head_office_private_pc_3: Computer = network.get_node_by_hostname(\"ST_HO-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Home office PC 1 --> ST Home Office PC 2\n", + "\n", + "st_head_office_private_pc_1.ping(st_head_office_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Home office PC 3 --> ST Router DR 2 \n", + "\n", + "st_head_office_private_pc_1.ping(st_intra_prv_rt_dr_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Home office PC 1 --> Public DNS\n", + "\n", + "st_head_office_private_pc_1.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Human Resources" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_human_resources_private_pc_1: Computer = network.get_node_by_hostname(\"ST_HR-PRV-PC-1\")\n", + "st_human_resources_private_pc_2: Computer = network.get_node_by_hostname(\"ST_HR-PRV-PC-2\")\n", + "st_human_resources_private_pc_3: Computer = network.get_node_by_hostname(\"ST_HR-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Human Resources PC 1 --> ST Human Resources PC 2 \n", + "\n", + "st_human_resources_private_pc_1.ping(st_human_resources_private_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Human Resources PC 2 --> # ST Human Resources PC 3\n", + "\n", + "st_human_resources_private_pc_2.ping(st_human_resources_private_pc_3.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Human Resources PC 1 --> ST Intranet Router DR 2 \n", + "\n", + "st_human_resources_private_pc_1.ping(st_intra_prv_rt_dr_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Human Resources PC 1 --> Public DNS \n", + "\n", + "st_human_resources_private_pc_1.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Data (Database / Database Backup)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_data_private_server_database: Server = network.get_node_by_hostname(\"ST_DATA-PRV-SRV-DB\")\n", + "st_data_private_server_storage: Server = network.get_node_by_hostname(\"ST_DATA-PRV-SRV-STORAGE\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Data Private Database --> ST Data Private Storage\n", + "\n", + "st_data_private_server_database.ping(st_data_private_server_storage.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Data Private Storage --> Public DNS\n", + "\n", + "st_data_private_server_storage.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Project A " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1: Computer = network.get_node_by_hostname(\"ST_PROJ-A-PRV-PC-1\")\n", + "st_project_a_private_pc_2: Computer = network.get_node_by_hostname(\"ST_PROJ-A-PRV-PC-2\")\n", + "st_project_a_private_pc_3: Computer = network.get_node_by_hostname(\"ST_PROJ-A-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 1 --> ST Private Project A PC 2\n", + "\n", + "st_project_a_private_pc_1.ping(st_project_a_private_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 2 --> ST Private Project A PC 3\n", + "\n", + "st_project_a_private_pc_3.ping(st_project_a_private_pc_3.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 3 --> Public DNS\n", + "\n", + "st_project_a_private_pc_3.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 1 --> ST_INTRA-PRV-RT-CR\n", + "\n", + "st_project_a_private_pc_1.ping(st_intra_prv_rt_cr.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project A PC 1 --> ST_INTRA-PRV-RT-DR-2\n", + "\n", + "st_project_a_private_pc_1.ping(\"192.168.170.2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Project B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_b_private_pc_1: Computer = network.get_node_by_hostname(\"ST_PROJ-B-PRV-PC-1\")\n", + "st_project_b_private_pc_2: Computer = network.get_node_by_hostname(\"ST_PROJ-B-PRV-PC-2\")\n", + "st_project_b_private_pc_3: Computer = network.get_node_by_hostname(\"ST_PROJ-B-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project B PC 1 --> ST Private Project B PC 2\n", + "\n", + "st_project_b_private_pc_1.ping(st_project_b_private_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project B PC 2 --> ST Private Project B PC 3\n", + "\n", + "st_project_b_private_pc_2.ping(st_project_b_private_pc_3.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project B PC 3 --> Public DNS\n", + "\n", + "st_project_b_private_pc_3.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Some Tech Project C" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_c_private_pc_1: Computer = network.get_node_by_hostname(\"ST_PROJ-C-PRV-PC-1\")\n", + "st_project_c_private_pc_2: Computer = network.get_node_by_hostname(\"ST_PROJ-C-PRV-PC-2\")\n", + "st_project_c_private_pc_3: Computer = network.get_node_by_hostname(\"ST_PROJ-C-PRV-PC-3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project C PC 1 --> ST Private Project C PC 2\n", + "\n", + "st_project_c_private_pc_1.ping(st_project_c_private_pc_2.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project C PC 2 --> ST Private Project C PC 3\n", + "\n", + "st_project_c_private_pc_2.ping(st_project_c_private_pc_3.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ST Private Project c PC 3 --> Public DNS\n", + "\n", + "st_project_c_private_pc_3.ping(isp_pub_srv_dns.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_c_private_pc_1.ping(st_project_b_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_b_private_pc_1.ping(st_project_a_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1.ping(st_head_office_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1.ping(st_human_resources_private_pc_1.network_interface[1].ip_address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UC7 Network | Applications & Services" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# DNS Server\n", + "\n", + "isp_pub_srv_dns.software_manager.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Web Server & Web Browser\n", + "\n", + "st_project_a_web_browser = st_project_a_private_pc_1.software_manager.software[\"web-browser\"]\n", + "st_project_a_web_browser.get_webpage()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_web_server = st_dmz_pub_srv_web.software_manager.software[\"web-server\"]\n", + "st_web_server.sys_log.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_database_client = st_project_a_private_pc_1.software_manager.software[\"database-client\"]\n", + "st_database_client.connect()\n", + "\n", + "st_database = st_data_private_server_database.software_manager.software[\"database-service\"]\n", + "st_database.sys_log.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_ftp_server = st_data_private_server_storage.software_manager.software[\"ftp-server\"]\n", + "\n", + "st_ftp_server.sys_log.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from primaite.simulator.system.services.dns.dns_client import DNSClient\n", + "home_pub_rt_dr.acl.show()\n", + "\n", + "home_pub_pc_1: Computer = network.get_node_by_hostname(\"HOME-PUB-PC-1\")\n", + "dns_client: DNSClient = home_pub_pc_1.software_manager.software[\"dns-client\"]\n", + "\n", + "dns_client.check_domain_exists(target_domain=\"some_tech.com\")\n", + "dns_client.dns_cache.get(\"some_tech.com\", None) is not None\n", + "len(dns_client.dns_cache) == 1\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for _ in range(80):\n", + " env.step(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def print_agent_actions_except_do_nothing(agent_name):\n", + " print(f\"\\n{agent_name} actions this episode:\")\n", + " for item in env.game.agents[agent_name].history:\n", + " if item.action != \"DONOTHING\":\n", + " node, application = 'unknown', 'unknown'\n", + " if (node_id := item.parameters.get('node_id')) is not None:\n", + " node = env.game.agents[agent_name].action_manager.node_names[node_id]\n", + " if (application_id := item.parameters.get('application_id')) is not None:\n", + " application = env.game.agents[agent_name].action_manager.application_names[node_id][application_id]\n", + " print(f\"Step: {item.timestep}, action: {item.action}, {node}, {application}, response: {item.response.status}\")\n", + "\n", + "print_agent_actions_except_do_nothing(\"HOME_WORKER-1-DB\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "green_agent = env.game.agents.get(\"HOME_WORKER-1-DB\")\n", + "green_agent.reward_function.total_reward" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print_agent_actions_except_do_nothing(\"HOME_WORKER-1-WEB\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "green_agent = env.game.agents.get(\"HOME_WORKER-1-WEB\")\n", + "green_agent.reward_function.total_reward" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "red_agent = env.game.agents.get(\"attacker\")\n", + "red_agent.logger.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_project_a_private_pc_1.file_system.show()\n", + "\n", + "# # st_project_a_private_pc_1.file_system.folders[\"exfiltration\"].show()\n", + "\n", + "# st_project_a_private_pc_1.software_manager.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "st_intra_prv_rt_cr.acl.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/primaite/notebooks/_package_data/uc7/blue_agent_action_and_obs.png b/src/primaite/notebooks/_package_data/uc7/blue_agent_action_and_obs.png new file mode 100644 index 00000000..85d5537d Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/blue_agent_action_and_obs.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_network.png b/src/primaite/notebooks/_package_data/uc7/uc7_network.png new file mode 100644 index 00000000..32cecb6a Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_network.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed.png b/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed.png new file mode 100644 index 00000000..c6d15435 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed_svg.svg b/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed_svg.svg new file mode 100644 index 00000000..dfa8cb3c --- /dev/null +++ b/src/primaite/notebooks/_package_data/uc7/uc7_network_detailed_svg.svg @@ -0,0 +1,4 @@ + + + +



ST_PROJ_A-
PRV-PC-1
ST_PROJ_A-...
SOME TECH PROJECT A (ST_PROJ_A)
SOME TECH PROJECT A (ST_PROJ_A)
192.168.240.0/29
192.168.240.0/29
3
3
2
2
1
1
9
9





ST_INTRA-PRV-
RT-DR-1
ST_INTRA-PRV-...
ST_INTRA-PRV-
RT-CR
ST_INTRA-PRV-...
ST_INTRA-PRV-
RT-DR-2
ST_INTRA-PRV-...
Port 1: 192.168.170.2
Port 1: 192.168.170.2
Port 3: 192.168.170.1
Port 3: 192.168.170.1


Port 2: 192.168.160.1
Port 2: 192.168.160.1
Port 1: 192.168.160.2
Port 1: 192.168.160.2
SOME_TECH HUMAN RESOURCES
(ST_HR)
SOME_TECH HUMAN RESOURCES...
ST_PROJ_C-
PRV-SW-AS
ST_PROJ_C-...
ST_PROJ_C-
PRV-PC-3
ST_PROJ_C-...
3
3
2
2
1
1
9
9
Port 1: 192.168.250.4
Port 1: 192.168.250.4
Port 4
Port 4
ST_PROJ_C-
PRV-PC-1
ST_PROJ_C-...
3
3
2
2
1
1
9
9
Port 1: 192.168.250.2
Port 1: 192.168.250.2
Port 2
Port 2
ST_PROJ_C-
PRV-PC-2
ST_PROJ_C-...
3
3
2
2
1
1
9
9
Port 1: 192.168.250.3
Port 1: 192.168.250.3
Port 3
Port 3
SOME TECH PROJECT C (ST_PROJ_C)
SOME TECH PROJECT C (ST_PROJ_C)
192.168.250.0/29
192.168.250.0/29
192.168.160.0/30
192.168.160.0/30
192.168.170.0/30
192.168.170.0/30
192.168.210.0/29
192.168.210.0/29




Port 4: 10.1.100.1
Port 4: 10.1.100.1
External Port: 10.1.100.2
External Port: 10.1.100.2
ST_HO-PRV-PC-2
ST_HO-PRV-PC-2
3
3
2
2
1
1
9
9
ST_HO-PRV-PC-1
ST_HO-PRV-PC-1
3
3
2
2
1
1
9
9
Port 2
Port 2
Port 1: 192.168.20.2
Port 1: 192.168.20.2
Port 3
Port 3
Port 1: 192.168.20.3
Port 1: 192.168.20.3
Port 4
Port 4
Port 1: 192.168.20.4
Port 1: 192.168.20.4
ST_PUB-FW
ST_PUB-FW
DMZ Port: 192.168.100.1
DMZ Port: 192.168.100.1
Port 1: 192.168.100.2
Port 1: 192.168.100.2
Internal Port: 192.168.150.1
Internal Port: 192.168.150.1
Port 1: 192.168.150.2 
Port 1: 192.168.150.2 
ST_HO-PRV-
SW-AS
ST_HO-PRV-...
ST_DATA-PRV-SRV-DB
ST_DATA-PRV-SRV-DB
7
7
9
9
3
3
6
6
Port 1: 192.168.220.3
Port 1: 192.168.220.3
Port 3
Port 3
Port 1: 192.168.220.2
Port 1: 192.168.220.2
Port 2
Port 2


ST_PROJ_B-
PRV-PC-2
ST_PROJ_B-...
3
3
2
2
1
1
9
9


ST_PROJ_B-
PRV-SW-AS
ST_PROJ_B-...
Port 1: 192.168.240.3
Port 1: 192.168.240.3
Port 3
Port 3

SOME_TECH DMZ (ST_DMZ)
SOME_TECH DMZ (ST_DMZ)
ST_DMZ-PUB-SRV-WEB
ST_DMZ-PUB-SRV-WEB
ST_DMZ-PUB-SRV-WEB
ST_DMZ-PUB-SRV-WEB
9
9
3
3
5
5
Port 1
Port 1
Port 2: 192.168.200.1
Port 2: 192.168.200.1
Port 1
Port 1
Port 3: 192.168.210.1
Port 3: 192.168.210.1
ST_HO-PRV-PC-1
ST_HO-PRV-PC-1
3
3
2
2
1
1
9
9
Port 2
Port 2
Port 1: 192.168.200.2
Port 1: 192.168.200.2
ST_HO-PRV-
SW-AS
ST_HO-PRV-...
ST_HO-PRV-PC-3
ST_HO-PRV-PC-3
3
3
2
2
1
1
9
9
Port 4
Port 4
Port 1: 192.168.200.4
Port 1: 192.168.200.4
SOME_TECH HEAD OFFICE
(ST_HO)
SOME_TECH HEAD OFFICE...
ST_HO-PRV-PC-2
ST_HO-PRV-PC-2
3
3
2
2
1
1
9
9
Port 3
Port 3
Port 1: 192.168.200.3
Port 1: 192.168.200.3
Port 4: 192.168.220.1
Port 4: 192.168.220.1
Port 1
Port 1
Port 1
Port 1
Port 4: 192.168.250.1
Port 4: 192.168.250.1
Port 2: 192.168.230.1
Port 2: 192.168.230.1
Port 1
Port 1
Port 1
Port 1
Port 3: 192.168.240.1 
Port 3: 192.168.240.1 
ST_PROJ_A-
PRV-SW-AS
ST_PROJ_A-...
ST_PROJ_A-
PRV-PC-2
ST_PROJ_A-...
3
3
2
2
1
1
9
9
ST_PROJ_A-
PRV-PC-3
ST_PROJ_A-...
3
3
2
2
1
1
9
9
Port 1: 192.168.230.2
Port 1: 192.168.230.2
Port 2
Port 2
Port 1: 192.168.230.3
Port 1: 192.168.230.3
Port 3
Port 3
Port 1: 192.168.230.4
Port 1: 192.168.230.4
Port 4
Port 4
SOME TECH PROJECT B
(ST_PROJ_ B)
SOME TECH PROJECT B...
ST_PROJ_B-
PRV-PC-1
ST_PROJ_B-...
3
3
2
2
1
1
9
9
Port 1: 192.168.240.2
Port 1: 192.168.240.2
Port 2
Port 2
Port 1: 192.168.240.4
Port 1: 192.168.240.4
Port 4
Port 4
ST_PROJ_B-
PRV-PC-3
ST_PROJ_B-...
3
3
2
2
1
1
9
9
ST_HO-PRV-PC-3
ST_HO-PRV-PC-3
3
3
2
2
1
1
9
9
SOME_TECH Main Site Network
SOME_TECH Main Site Network
SOME TECH DATA (ST_DATA)
SOME TECH DATA (ST_DATA)



HOME OFFICE (HOME)
HOME OFFICE (HOME)


HOME-PUB-PC-1
HOME-PUB-PC-1
3
3
2
2
1
1
9
9
192.168.1.0/26
192.168.1.0/26
HOME-PUB-PC-2
HOME-PUB-PC-2
3
3
2
2
1
1
9
9
HOME-PUB-SRV
HOME-PUB-SRV
3
3
9
9
HOME-PUB-
SW-AS
HOME-PUB-...
Port 1: 192.168.1.2/26
Port 1: 192.168.1.2/26
Port 2
Port 2
Port 1: 192.168.1.3/26
Port 1: 192.168.1.3/26
Port 3
Port 3
Port 1
Port 1
Port 1: 192.168.1.1
Port 1: 192.168.1.1
HOME-PUB-
RT-DR
HOME-PUB-...
ISP-PUB-RT-BR
ISP-PUB-RT-BR


ISP-PUB-
SRV-DNS
ISP-PUB-...
8.8.8.0/28
8.8.8.0/28
10.1.10.0/30
10.1.10.0/30
INTERNET (ISP)
INTERNET (ISP)
REM-PUB-FW
REM-PUB-FW
Port 1: 192.168.1.4/26
Port 1: 192.168.1.4/26
Port 4
Port 4
REM-PUB-RT-DR
REM-PUB-RT-DR


Port 3: 10.1.10.1
Port 3: 10.1.10.1
External Port: 10.1.10.1
External Port: 10.1.10.1
Port 2: 10.1.0.2
Port 2: 10.1.0.2
Port 1: 10.1.0.1
Port 1: 10.1.0.1
Internal Port: 192.168.10.1
Internal Port: 192.168.10.1
Port 1: 192.168.10.2
Port 1: 192.168.10.2
Port 1: 8.8.8.8
Port 1: 8.8.8.8
Port 2: 8.8.8.1
Port 2: 8.8.8.1
4
4
10
10


Remote Site (REMOTE)
Remote Site (REMOTE)
REM-PUB-PC-2
REM-PUB-PC-2
3
3
2
2
1
1
9
9
REM-PUB-PC-1
REM-PUB-PC-1
3
3
2
2
1
1
9
9
Port 2
Port 2
Port 1: 192.168.20.2
Port 1: 192.168.20.2
Port 3
Port 3
Port 1: 192.168.20.3
Port 1: 192.168.20.3
Port 3
Port 3
Port 2: 192.168.20.1
Port 2: 192.168.20.1
Port 1
Port 1
REM-PUB-
SW-AS
REM-PUB-...
REM-PUB-SRV
REM-PUB-SRV
3
3
9
9
Port 1: 192.168.20.4
Port 1: 192.168.20.4
Port 4
Port 4
192.168.10.0/30
192.168.10.0/30
192.168.10.0/30
192.168.10.0/30
192.168.250.0/29
192.168.250.0/29
ST_DATA-PRV-SW-AS
ST_DATA-PRV-SW-AS
ST_DATA-PRV-SRV-STORAGE
ST_DATA-PRV-SRV-STOR...
3
3
8
8
9
9
192.168.220.0/29
192.168.220.0/29
192.168.200.0/29
192.168.200.0/29
192.168.50.0/28
192.168.50.0/28
10.1.100.0/30
10.1.100.0/30
192.168.100.0/30
192.168.100.0/30
10.1.0.0/30
10.1.0.0/30
Node Software
Number Key
Node Software...
SERVICES
SERVICES
APPLICATIONS
APPLICATIONS
1
1
2
2
DatabaseClient
DatabaseClient
Webbrowser
Webbrowser
3
3
DNSClient
DNSClient
4
4
Web Server
Web Server
5
5
WebServer
WebServer
6
6
DatabaseService
DatabaseService
7
7
FTP Client
FTP Client
8
8
FTP Server
FTP Server
9
9
NTP Client
NTP Client
10
10
NTP Server
NTP Server

UC7 Detailed Network Diagram

UC7 Detailed Network Diagram
Node Type Colour Key
Node Type Colour Key
ROUTER
#CFE4FF
ROUTER...
SERVER
#CED4DB
SERVER...
FIREWALL
#FFD9D9
FIREWALL...
SWITCH
#FFF7A1
SWITCH...
COMPUTER
#E9E9E9
COMPUTER...
Text is not SVG - cannot display
diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_activate.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_activate.png new file mode 100644 index 00000000..b8f283e4 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_activate.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_c2.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_c2.png new file mode 100644 index 00000000..b8ab98e6 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_c2.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_download_install.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_download_install.png new file mode 100644 index 00000000..4d7a5f20 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_download_install.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_main.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_main.png new file mode 100644 index 00000000..4d86d0a7 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_main.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_payload.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_payload.png new file mode 100644 index 00000000..bf9f07ae Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_payload.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_1.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_1.png new file mode 100644 index 00000000..1b6b6a1d Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_1.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_2.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_2.png new file mode 100644 index 00000000..4faa8eb4 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_2.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_3.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_3.png new file mode 100644 index 00000000..1f00d3a8 Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_3.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_4.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_4.png new file mode 100644 index 00000000..01cddf5c Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap001/uc7_tap001_propagate_4.png differ diff --git a/src/primaite/notebooks/_package_data/uc7/uc7_tap003/uc7_tap003_main.png b/src/primaite/notebooks/_package_data/uc7/uc7_tap003/uc7_tap003_main.png new file mode 100644 index 00000000..2882fd3b Binary files /dev/null and b/src/primaite/notebooks/_package_data/uc7/uc7_tap003/uc7_tap003_main.png differ diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 9cc39848..2adb9ff5 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -1534,6 +1534,12 @@ class Node(SimComponent, ABC): _registry: ClassVar[Dict[str, Type["Node"]]] = {} """Registry of application types. Automatically populated when subclasses are defined.""" + red_scan_countdown: int = 0 + "Time steps until reveal to red scan is complete." + + node_scan_countdown: int = 0 + "Time steps until scan is complete" + # TODO: this should not be set for abstract classes. _discriminator: ClassVar[str] """discriminator for this particular class, used for printing and logging. Each subclass redefines this.""" @@ -1570,12 +1576,6 @@ class Node(SimComponent, ABC): node_scan_duration: int = 10 "How many timesteps until the whole node is scanned. Default 10 time steps." - node_scan_countdown: int = 0 - "Time steps until scan is complete" - - red_scan_countdown: int = 0 - "Time steps until reveal to red scan is complete." - dns_server: Optional[IPv4Address] = None "List of IP addresses of DNS servers used for name resolution." @@ -2019,10 +2019,10 @@ class Node(SimComponent, ABC): # time steps which require the node to be on if self.operating_state == NodeOperatingState.ON: # node scanning - if self.config.node_scan_countdown > 0: - self.config.node_scan_countdown -= 1 + if self.node_scan_countdown > 0: + self.node_scan_countdown -= 1 - if self.config.node_scan_countdown == 0: + if self.node_scan_countdown == 0: # scan everything! for process_id in self.processes: self.processes[process_id].scan() @@ -2038,10 +2038,10 @@ class Node(SimComponent, ABC): # scan file system self.file_system.scan(instant_scan=True) - if self.config.red_scan_countdown > 0: - self.config.red_scan_countdown -= 1 + if self.red_scan_countdown > 0: + self.red_scan_countdown -= 1 - if self.config.red_scan_countdown == 0: + if self.red_scan_countdown == 0: # scan processes for process_id in self.processes: self.processes[process_id].reveal_to_red() @@ -2098,7 +2098,7 @@ class Node(SimComponent, ABC): to the red agent. """ - self.config.node_scan_countdown = self.config.node_scan_duration + self.node_scan_countdown = self.config.node_scan_duration return True def reveal_to_red(self) -> bool: @@ -2114,7 +2114,7 @@ class Node(SimComponent, ABC): `revealed_to_red` to `True`. """ - self.config.red_scan_countdown = self.config.node_scan_duration + self.red_scan_countdown = self.config.node_scan_duration return True def power_on(self) -> bool: diff --git a/tests/e2e_integration_tests/test_uc7_agents.py b/tests/e2e_integration_tests/test_uc7_agents.py new file mode 100644 index 00000000..d54bf6d2 --- /dev/null +++ b/tests/e2e_integration_tests/test_uc7_agents.py @@ -0,0 +1,172 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK + +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG, load +from primaite.game.game import PrimaiteGame +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.file_system.file import File +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.network.container import Network +from primaite.simulator.network.hardware.nodes.host.computer import Computer +from primaite.simulator.network.hardware.nodes.host.server import Server +from primaite.simulator.network.hardware.nodes.network.firewall import Firewall +from primaite.simulator.system.applications.application import ApplicationOperatingState +from primaite.simulator.system.applications.database_client import DatabaseClient +from primaite.simulator.system.applications.red_applications.c2.c2_beacon import C2Beacon +from primaite.simulator.system.applications.red_applications.ransomware_script import RansomwareScript +from primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.system.services.database.database_service import DatabaseService +from primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.system.services.dns.dns_server import DNSServer +from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.ftp.ftp_server import FTPServer +from primaite.simulator.system.services.ntp.ntp_client import NTPClient +from primaite.simulator.system.services.ntp.ntp_server import NTPServer +from primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.system.software import SoftwareHealthState + +CONFIG_FILE = _EXAMPLE_CFG / "uc7_config.yaml" +ATTACK_AGENT_INDEX = 32 + + +@pytest.fixture(scope="function") +def uc7_environment() -> PrimaiteGymEnv: + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def assert_agent_reward(env: PrimaiteGymEnv, agent_name: str, positive: bool): + """Asserts that a given agent has a reward that is below/above or equal to 0 dependant on arguments.""" + agent_reward = env.game.agents[agent_name].reward_function.total_reward + if agent_name == "defender": + return # ignore blue agent + if positive is True: + assert agent_reward >= 0 # Asserts that no agents are below a total reward of 0 + elif positive is False: + assert agent_reward <= 0 # Asserts that no agents are above a total reward of 0 + + +def test_green_agent_positive_reward(uc7_environment): + """Confirms that the UC7 Green Agents receive a positive reward (Default Behaviour).""" + env: PrimaiteGymEnv = uc7_environment + + # Performing no changes to the environment. Default Behaviour + + # Stepping 60 times in the environment + for _ in range(60): + env.step(0) + + for agent in env.game.agents: + assert_agent_reward(env=env, agent_name=env.game.agents[agent].config.ref, positive=True) + + +def test_green_agent_negative_reward(uc7_environment): + """Confirms that the UC7 Green Agents receive a negative reward. (Disabled web-server and database-service)""" + + env: PrimaiteGymEnv = uc7_environment + + # Purposefully disabling the following services: + + # 1. Disabling the web-server + st_dmz_pub_srv_web: Server = env.game.simulation.network.get_node_by_hostname("ST_DMZ-PUB-SRV-WEB") + st_web_server = st_dmz_pub_srv_web.software_manager.software["web-server"] + st_web_server.operating_state = ServiceOperatingState.DISABLED + assert st_web_server.operating_state == ServiceOperatingState.DISABLED + + # 2. Disabling the DatabaseServer + st_data_database_server: Server = env.game.simulation.network.get_node_by_hostname("ST_DATA-PRV-SRV-DB") + database_service: DatabaseService = st_data_database_server.software_manager.software["database-service"] + database_service.operating_state = ServiceOperatingState.DISABLED + assert database_service.operating_state == ServiceOperatingState.DISABLED + + # Stepping 100 times in the environment + for _ in range(100): + env.step(0) + + for agent in env.game.agents: + assert_agent_reward(env=env, agent_name=env.game.agents[agent].config.ref, positive=False) + + +def test_tap001_default_behaviour(uc7_environment): + """Confirms that the TAP001 expected simulation impacts works as expected in the UC7 environment.""" + env: PrimaiteGymEnv = uc7_environment + env.reset() + network = env.game.simulation.network + + # Running for 128 episodes + for _ in range(128): + env.step(0) + + some_tech_proj_a_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-A-PRV-PC-1") + + # Asserting that the `malware_dropper.ps1` was created. + + malware_dropper_file: File = some_tech_proj_a_pc_1.file_system.get_file("downloads", "malware_dropper.ps1") + assert malware_dropper_file.health_status == FileSystemItemHealthStatus.GOOD + + # Asserting that the `RansomwareScript` launched successfully. + + ransomware_script: RansomwareScript = some_tech_proj_a_pc_1.software_manager.software["ransomware-script"] + assert ransomware_script.health_state_actual == SoftwareHealthState.GOOD + assert ransomware_script.operating_state == ApplicationOperatingState.RUNNING + + # Asserting that the `C2Beacon` connected to the `C2Server`. + + c2_beacon: C2Beacon = some_tech_proj_a_pc_1.software_manager.software["c2-beacon"] + assert c2_beacon.health_state_actual == SoftwareHealthState.GOOD + assert c2_beacon.operating_state == ApplicationOperatingState.RUNNING + assert c2_beacon.c2_connection_active == True + + # Asserting that the target database was successfully corrupted. + some_tech_data_server_database: Server = network.get_node_by_hostname("ST_DATA-PRV-SRV-DB") + database_file: File = some_tech_data_server_database.file_system.get_file( + folder_name="database", file_name="database.db" + ) + assert database_file.health_status == FileSystemItemHealthStatus.CORRUPT + + +def test_tap003_default_behaviour(uc7_environment): + """Confirms that the TAP003 expected simulation impacts works as expected in the UC7 environment.""" + from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router + from primaite.simulator.network.transmission.network_layer import IPPacket, IPProtocol + from primaite.utils.validation.port import PORT_LOOKUP + + def uc7_environment_tap003() -> PrimaiteGymEnv: + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["starting_nodes"] = ["ST_PROJ-A-PRV-PC-1"] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["default_starting_node"] = "ST_PROJ-A-PRV-PC-1" + env = PrimaiteGymEnv(env_config=cfg) + return env + + env: PrimaiteGymEnv = uc7_environment_tap003() + env.reset() + # Running for 128 episodes + for _ in range(128): + env.step(0) + network = env.game.simulation.network + + # Asserting that a malicious ACL has been added to ST_INTRA-PRV-RT-DR-1 + st_intra_prv_rt_dr_1: Router = network.get_node_by_hostname(hostname="ST_INTRA-PRV-RT-DR-1") + assert st_intra_prv_rt_dr_1.acl.acl[1].action == ACLAction.DENY + assert st_intra_prv_rt_dr_1.acl.acl[1].protocol == "tcp" + assert st_intra_prv_rt_dr_1.acl.acl[1].src_port == PORT_LOOKUP.get("POSTGRES_SERVER") + assert st_intra_prv_rt_dr_1.acl.acl[1].dst_port == PORT_LOOKUP.get("POSTGRES_SERVER") + + # Asserting that a malicious ACL has been added to ST_INTRA-PRV-RT-CR + st_intra_prv_rt_cr: Router = network.get_node_by_hostname(hostname="ST_INTRA-PRV-RT-CR") + assert st_intra_prv_rt_cr.acl.acl[1].action == ACLAction.DENY + assert st_intra_prv_rt_cr.acl.acl[1].protocol == "tcp" + assert st_intra_prv_rt_cr.acl.acl[1].src_port == PORT_LOOKUP.get("HTTP") + assert st_intra_prv_rt_cr.acl.acl[1].dst_port == PORT_LOOKUP.get("HTTP") + + # Asserting that a malicious ACL has been added to REM-PUB-RT-DR + rem_pub_rt_dr: Router = network.get_node_by_hostname(hostname="REM-PUB-RT-DR") + assert rem_pub_rt_dr.acl.acl[1].action == ACLAction.DENY + assert rem_pub_rt_dr.acl.acl[1].protocol == "tcp" + assert rem_pub_rt_dr.acl.acl[1].src_port == PORT_LOOKUP.get("DNS") + assert rem_pub_rt_dr.acl.acl[1].dst_port == PORT_LOOKUP.get("DNS") diff --git a/tests/e2e_integration_tests/test_uc7_route_connectivity.py b/tests/e2e_integration_tests/test_uc7_route_connectivity.py new file mode 100644 index 00000000..af5f3cd2 --- /dev/null +++ b/tests/e2e_integration_tests/test_uc7_route_connectivity.py @@ -0,0 +1,237 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.game import PrimaiteGame +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.network.container import Network +from primaite.simulator.network.hardware.nodes.host.computer import Computer +from primaite.simulator.network.hardware.nodes.host.server import Server +from primaite.simulator.network.hardware.nodes.network.firewall import Firewall +from primaite.simulator.network.hardware.nodes.network.router import Router +from primaite.simulator.network.hardware.nodes.network.switch import Switch + +CONFIG_FILE = _EXAMPLE_CFG / "uc7_config.yaml" + + +@pytest.fixture(scope="function") +def uc7_network() -> Network: + with open(file=CONFIG_FILE, mode="r") as f: + cfg = yaml.safe_load(stream=f) + + game = PrimaiteGame.from_config(cfg=cfg) + return game.simulation.network + + +def test_ping_home_office(uc7_network): + """Asserts that all home_pub_* can ping each-other and the public dns (isp_pub_srv_dns)""" + network = uc7_network + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + home_pub_pc_2: Computer = network.get_node_by_hostname("HOME-PUB-PC-2") + home_pub_pc_srv: Server = network.get_node_by_hostname("HOME-PUB-SRV") + home_pub_rt_dr: Router = network.get_node_by_hostname("HOME-PUB-RT-DR") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + assert home_pub_pc_1.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + def ping_all_home_office(host): + assert host.ping(home_pub_pc_1.network_interface[1].ip_address) + assert host.ping(home_pub_pc_2.network_interface[1].ip_address) + assert host.ping(home_pub_pc_srv.network_interface[1].ip_address) + assert host.ping(home_pub_rt_dr.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_home_office(home_pub_pc_1) + ping_all_home_office(home_pub_pc_2) + ping_all_home_office(home_pub_pc_srv) + ping_all_home_office(isp_pub_srv_dns) + + +def test_ping_remote_site(uc7_network): + """Asserts that all remote_pub_* hosts can ping each-other and the public dns server (isp_pub_srv_dns)""" + network = uc7_network + rem_pub_fw: Firewall = network.get_node_by_hostname(hostname="REM-PUB-FW") + rem_pub_rt_dr: Router = network.get_node_by_hostname(hostname="REM-PUB-RT-DR") + rem_pub_pc_1: Computer = network.get_node_by_hostname(hostname="REM-PUB-PC-1") + rem_pub_pc_2: Computer = network.get_node_by_hostname(hostname="REM-PUB-PC-2") + rem_pub_srv: Computer = network.get_node_by_hostname(hostname="REM-PUB-SRV") + + def ping_all_remote_site(host): + assert host.ping(rem_pub_fw.network_interface[1].ip_address) + assert host.ping(rem_pub_rt_dr.network_interface[1].ip_address) + assert host.ping(rem_pub_pc_1.network_interface[1].ip_address) + assert host.ping(rem_pub_pc_2.network_interface[1].ip_address) + assert host.ping(rem_pub_srv.network_interface[1].ip_address) + + ping_all_remote_site(host=rem_pub_fw) + ping_all_remote_site(host=rem_pub_rt_dr) + ping_all_remote_site(host=rem_pub_pc_1) + ping_all_remote_site(host=rem_pub_pc_2) + ping_all_remote_site(host=rem_pub_srv) + + +def test_ping_some_tech_dmz(uc7_network): + """Asserts that the st_dmz_pub_srv_web and the st_public_firewall can ping each other and remote site and home office.""" + network = uc7_network + st_pub_fw: Firewall = network.get_node_by_hostname(hostname="ST_PUB-FW") + st_dmz_pub_srv_web: Server = network.get_node_by_hostname(hostname="ST_DMZ-PUB-SRV-WEB") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + + def ping_all_some_tech_dmz(host): + assert host.ping(st_dmz_pub_srv_web.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_dmz(host=st_pub_fw) + ping_all_some_tech_dmz(host=isp_pub_srv_dns) + ping_all_some_tech_dmz(host=home_pub_pc_1) + + +def test_ping_some_tech_head_office(uc7_network): + """Asserts that all the some_tech_* PCs can ping each other and the public dns""" + network = uc7_network + st_home_office_private_pc_1: Computer = network.get_node_by_hostname("ST_HO-PRV-PC-1") + st_home_office_private_pc_2: Computer = network.get_node_by_hostname("ST_HO-PRV-PC-2") + st_home_office_private_pc_3: Computer = network.get_node_by_hostname("ST_HO-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_head_office(host): + assert host.ping(st_home_office_private_pc_1.network_interface[1].ip_address) + assert host.ping(st_home_office_private_pc_2.network_interface[1].ip_address) + assert host.ping(st_home_office_private_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_head_office(host=st_home_office_private_pc_1) + ping_all_some_tech_head_office(host=st_home_office_private_pc_2) + ping_all_some_tech_head_office(host=st_home_office_private_pc_3) + + +def test_ping_some_tech_hr(uc7_network): + """Assert that all some_tech_hr_* PCs can ping each other and the public dns""" + network = uc7_network + some_tech_hr_pc_1: Computer = network.get_node_by_hostname("ST_HR-PRV-PC-1") + some_tech_hr_pc_2: Computer = network.get_node_by_hostname("ST_HR-PRV-PC-2") + some_tech_hr_pc_3: Computer = network.get_node_by_hostname("ST_HR-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_hr(host): + assert host.ping(some_tech_hr_pc_1.network_interface[1].ip_address) + assert host.ping(some_tech_hr_pc_2.network_interface[1].ip_address) + assert host.ping(some_tech_hr_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_hr(some_tech_hr_pc_1) + ping_all_some_tech_hr(some_tech_hr_pc_2) + ping_all_some_tech_hr(some_tech_hr_pc_3) + + +def test_some_tech_data_hr(uc7_network): + """Assert that all some_tech_data_* servers can ping each other and the public dns.""" + network = uc7_network + some_tech_data_server_storage: Server = network.get_node_by_hostname("ST_DATA-PRV-SRV-STORAGE") + some_tech_data_server_database: Server = network.get_node_by_hostname("ST_DATA-PRV-SRV-DB") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_hr(host): + assert host.ping(some_tech_data_server_storage.network_interface[1].ip_address) + assert host.ping(some_tech_data_server_database.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_hr(some_tech_data_server_storage) + ping_all_some_tech_hr(some_tech_data_server_database) + + +def test_some_tech_project_a(uc7_network): + """Asserts that all some_tech project A's PCs can ping each other and the public dns.""" + network = uc7_network + some_tech_proj_a_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-A-PRV-PC-1") + some_tech_proj_a_pc_2: Computer = network.get_node_by_hostname("ST_PROJ-A-PRV-PC-2") + some_tech_proj_a_pc_3: Computer = network.get_node_by_hostname("ST_PROJ-A-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_proj_a(host): + assert host.ping(some_tech_proj_a_pc_1.network_interface[1].ip_address) + assert host.ping(some_tech_proj_a_pc_2.network_interface[1].ip_address) + assert host.ping(some_tech_proj_a_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_proj_a(some_tech_proj_a_pc_1) + ping_all_some_tech_proj_a(some_tech_proj_a_pc_2) + ping_all_some_tech_proj_a(some_tech_proj_a_pc_3) + + +def test_some_tech_project_b(uc7_network): + """Asserts that all some_tech_project_b PC's can ping each other and the public dps.""" + network = uc7_network + some_tech_proj_b_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-B-PRV-PC-1") + some_tech_proj_b_pc_2: Computer = network.get_node_by_hostname("ST_PROJ-B-PRV-PC-2") + some_tech_proj_b_pc_3: Computer = network.get_node_by_hostname("ST_PROJ-B-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_proj_b(host): + assert host.ping(some_tech_proj_b_pc_1.network_interface[1].ip_address) + assert host.ping(some_tech_proj_b_pc_2.network_interface[1].ip_address) + assert host.ping(some_tech_proj_b_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_proj_b(some_tech_proj_b_pc_1) + ping_all_some_tech_proj_b(some_tech_proj_b_pc_2) + ping_all_some_tech_proj_b(some_tech_proj_b_pc_3) + + +def test_some_tech_project_a(uc7_network): + """Asserts that all some_tech_project_c PC's can ping each other and the public dps.""" + network = uc7_network + some_tech_proj_c_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-C-PRV-PC-1") + some_tech_proj_c_pc_2: Computer = network.get_node_by_hostname("ST_PROJ-C-PRV-PC-2") + some_tech_proj_c_pc_3: Computer = network.get_node_by_hostname("ST_PROJ-C-PRV-PC-3") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + def ping_all_some_tech_proj_c(host): + assert host.ping(some_tech_proj_c_pc_1.network_interface[1].ip_address) + assert host.ping(some_tech_proj_c_pc_2.network_interface[1].ip_address) + assert host.ping(some_tech_proj_c_pc_3.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + + ping_all_some_tech_proj_c(some_tech_proj_c_pc_1) + ping_all_some_tech_proj_c(some_tech_proj_c_pc_2) + ping_all_some_tech_proj_c(some_tech_proj_c_pc_3) + + +def test_ping_all_networks(uc7_network): + """Asserts that one machine from each network is able to ping all others.""" + network = uc7_network + home_office_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + remote_office_pc_1: Computer = network.get_node_by_hostname("REM-PUB-PC-1") + st_head_office_pc_1: Computer = network.get_node_by_hostname("ST_HO-PRV-PC-1") + st_human_resources_pc_1: Computer = network.get_node_by_hostname("ST_HR-PRV-PC-1") + st_data_storage_server: Server = network.get_node_by_hostname("ST_DATA-PRV-SRV-STORAGE") + st_data_database_server: Server = network.get_node_by_hostname("ST_DATA-PRV-SRV-DB") + st_proj_a_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-A-PRV-PC-1") + st_proj_b_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-B-PRV-PC-1") + st_proj_c_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-C-PRV-PC-1") + + def ping_network_wide(host): + assert host.ping(home_office_pc_1.network_interface[1].ip_address) + assert host.ping(isp_pub_srv_dns.network_interface[1].ip_address) + assert host.ping(remote_office_pc_1.network_interface[1].ip_address) + assert host.ping(st_head_office_pc_1.network_interface[1].ip_address) + assert host.ping(st_human_resources_pc_1.network_interface[1].ip_address) + assert host.ping(st_data_storage_server.network_interface[1].ip_address) + assert host.ping(st_data_database_server.network_interface[1].ip_address) + assert host.ping(st_proj_a_pc_1.network_interface[1].ip_address) + assert host.ping(st_proj_b_pc_1.network_interface[1].ip_address) + assert host.ping(st_proj_c_pc_1.network_interface[1].ip_address) + + ping_network_wide(host=home_office_pc_1) + ping_network_wide(host=isp_pub_srv_dns) + ping_network_wide(host=remote_office_pc_1) + ping_network_wide(host=st_head_office_pc_1) + ping_network_wide(host=st_human_resources_pc_1) + ping_network_wide(host=st_data_storage_server) + ping_network_wide(host=st_data_database_server) + ping_network_wide(host=st_proj_a_pc_1) + ping_network_wide(host=st_proj_b_pc_1) + ping_network_wide(host=st_proj_c_pc_1) diff --git a/tests/e2e_integration_tests/test_uc7_services_and_applications.py b/tests/e2e_integration_tests/test_uc7_services_and_applications.py new file mode 100644 index 00000000..0bcfcfca --- /dev/null +++ b/tests/e2e_integration_tests/test_uc7_services_and_applications.py @@ -0,0 +1,338 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK + +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.game import PrimaiteGame +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.network.container import Network +from primaite.simulator.network.hardware.nodes.host.computer import Computer +from primaite.simulator.network.hardware.nodes.host.server import Server +from primaite.simulator.system.applications.application import ApplicationOperatingState +from primaite.simulator.system.applications.database_client import DatabaseClient +from primaite.simulator.system.applications.web_browser import WebBrowser +from primaite.simulator.system.services.database.database_service import DatabaseService +from primaite.simulator.system.services.dns.dns_client import DNSClient +from primaite.simulator.system.services.dns.dns_server import DNSServer +from primaite.simulator.system.services.ftp.ftp_client import FTPClient +from primaite.simulator.system.services.ftp.ftp_server import FTPServer +from primaite.simulator.system.services.ntp.ntp_client import NTPClient +from primaite.simulator.system.services.ntp.ntp_server import NTPServer +from primaite.simulator.system.services.service import ServiceOperatingState +from primaite.simulator.system.software import SoftwareHealthState + +CONFIG_FILE = _EXAMPLE_CFG / "uc7_config.yaml" + + +@pytest.fixture(scope="function") +def uc7_network() -> Network: + with open(file=CONFIG_FILE, mode="r") as f: + cfg = yaml.safe_load(stream=f) + + game = PrimaiteGame.from_config(cfg=cfg) + return game.simulation.network + + +def assert_ntp_client(host): + """Confirms that the ntp_client service is present and functioning.""" + ntp_client: NTPClient = host.software_manager.software["ntp-client"] + assert ntp_client is not None + assert ntp_client.operating_state == ServiceOperatingState.RUNNING + assert ntp_client.health_state_actual == SoftwareHealthState.GOOD + + +def assert_dns_client(host): + """Confirms that the dns_client service is present and functioning.""" + dns_client: DNSClient = host.software_manager.software["dns-client"] + assert dns_client is not None + assert dns_client.operating_state == ServiceOperatingState.RUNNING + assert dns_client.health_state_actual == SoftwareHealthState.GOOD + + +def assert_web_browser(host: Computer): + """Asserts that the web_browser application is present and functioning.""" + web_browser: WebBrowser = host.software_manager.software["web-browser"] + assert web_browser is not None + assert web_browser.operating_state == ApplicationOperatingState.RUNNING + assert web_browser.health_state_actual == SoftwareHealthState.GOOD + + +def assert_database_client(host: Computer): + """Asserts that the database_client application is present and functioning.""" + database_client = host.software_manager.software["database-client"] + assert database_client is not None + assert database_client.operating_state == ApplicationOperatingState.RUNNING + assert database_client.health_state_actual == SoftwareHealthState.GOOD + + +def test_home_office_software(uc7_network): + """Asserts that each host in the home_office network contains the expected software.""" + network: Network = uc7_network + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + home_pub_pc_2: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + home_pub_srv: Server = network.get_node_by_hostname("HOME-PUB-SRV") + + # Home Office PC 1 + assert_web_browser(home_pub_pc_1) + assert_database_client(home_pub_pc_1) + assert_dns_client(home_pub_pc_1) + assert_ntp_client(home_pub_pc_1) + + # Home Office PC 2 + assert_web_browser(home_pub_pc_2) + assert_database_client(home_pub_pc_2) + assert_dns_client(home_pub_pc_2) + assert_ntp_client(home_pub_pc_2) + + # Home Office Server + assert_dns_client(home_pub_srv) + assert_ntp_client(home_pub_srv) + + +def test_internet_dns_server(uc7_network): + """Asserts that `ISP-PUB-SRV-DNS` host's DNSServer application is operating and functioning as expected.""" + network: Network = uc7_network + isp_pub_srv_dns: Server = network.get_node_by_hostname("ISP-PUB-SRV-DNS") + + # Confirming that the DNSServer is up and running: + + dns_server: DNSServer = isp_pub_srv_dns.software_manager.software["dns-server"] + assert dns_server is not None + assert dns_server.operating_state == ServiceOperatingState.RUNNING + assert dns_server.health_state_actual == SoftwareHealthState.GOOD + + # Confirming that the DNSServer is performing as expected by performing a request from a client + + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + dns_client: DNSClient = home_pub_pc_1.software_manager.software["dns-client"] + + assert dns_client.check_domain_exists(target_domain="some_tech.com") + assert dns_client.dns_cache.get("some_tech.com", None) is not None + assert len(dns_client.dns_cache) == 1 + + +def test_remote_office_software(uc7_network): + """Asserts that each host on the remote_office network has the expected services & applications which are operating as expected.""" + network = uc7_network + rem_pub_pc_1: Computer = network.get_node_by_hostname(hostname="REM-PUB-PC-1") + rem_pub_pc_2: Computer = network.get_node_by_hostname(hostname="REM-PUB-PC-2") + rem_pub_srv: Server = network.get_node_by_hostname(hostname="REM-PUB-SRV") + + # Remote Site PC 1 + assert_web_browser(rem_pub_pc_1) + assert_database_client(rem_pub_pc_1) + assert_dns_client(rem_pub_pc_1) + assert_ntp_client(rem_pub_pc_1) + + # Remote Site PC 2 + assert_web_browser(rem_pub_pc_2) + assert_database_client(rem_pub_pc_2) + assert_dns_client(rem_pub_pc_2) + assert_ntp_client(rem_pub_pc_2) + + # Remote Site Server + assert_dns_client(rem_pub_srv) + assert_ntp_client(rem_pub_srv) + + +def test_dmz_web_server(uc7_network): + """Asserts that the DMZ WebServer functions as expected""" + network: Network = uc7_network + st_dmz_pub_srv_web: Server = network.get_node_by_hostname("ST_DMZ-PUB-SRV-WEB") + + # Asserting the ST Web Server is working as expected + st_web_server = st_dmz_pub_srv_web.software_manager.software["web-server"] + assert st_web_server is not None + assert st_web_server.operating_state == ServiceOperatingState.RUNNING + assert st_web_server.health_state_actual == SoftwareHealthState.GOOD + + # Asserting that WebBrowser can actually connect to the WebServer + + # SOME TECH Human Resources --> DMZ Web Server + st_hr_pc_1: Computer = network.get_node_by_hostname("ST_HR-PRV-PC-1") + st_hr_pc_1_web_browser: WebBrowser = st_hr_pc_1.software_manager.software["web-browser"] + assert st_hr_pc_1_web_browser.get_webpage("http://some_tech.com") + + # Remote Site --> DMZ Web Server + rem_pub_pc_1: Computer = network.get_node_by_hostname("REM-PUB-PC-1") + rem_pub_pc_1_web_browser: WebBrowser = rem_pub_pc_1.software_manager.software["web-browser"] + assert rem_pub_pc_1_web_browser.get_webpage("http://some_tech.com") + + # Home Office --> DMZ Web Server + home_pub_pc_1: Computer = network.get_node_by_hostname("HOME-PUB-PC-1") + home_pub_pc_1_web_browser: WebBrowser = home_pub_pc_1.software_manager.software["web-browser"] + assert home_pub_pc_1_web_browser.get_webpage("http://some_tech.com") + + +def test_tech_head_office_software(uc7_network): + """Asserts that each host on the some_tech_head_office network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + + st_head_office_private_pc_1: Computer = network.get_node_by_hostname("ST_HO-PRV-PC-1") + st_head_office_private_pc_2: Computer = network.get_node_by_hostname("ST_HO-PRV-PC-2") + st_head_office_private_pc_3: Computer = network.get_node_by_hostname("ST_HO-PRV-PC-3") + + # ST Head Office One + + assert_web_browser(st_head_office_private_pc_1) + assert_database_client(st_head_office_private_pc_1) + assert_dns_client(st_head_office_private_pc_1) + assert_ntp_client(st_head_office_private_pc_1) + + # ST Head Office Two + + assert_web_browser(st_head_office_private_pc_2) + assert_database_client(st_head_office_private_pc_2) + assert_dns_client(st_head_office_private_pc_2) + assert_ntp_client(st_head_office_private_pc_2) + + # ST Head Office Three + + assert_web_browser(st_head_office_private_pc_3) + assert_database_client(st_head_office_private_pc_3) + assert_dns_client(st_head_office_private_pc_3) + assert_ntp_client(st_head_office_private_pc_3) + + +def test_tech_human_resources_office_software(uc7_network): + """Asserts that each host on the some_tech human_resources network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + + st_hr_pc_1: Computer = network.get_node_by_hostname("ST_HR-PRV-PC-1") + st_hr_pc_2: Computer = network.get_node_by_hostname("ST_HR-PRV-PC-2") + st_hr_pc_3: Computer = network.get_node_by_hostname("ST_HR-PRV-PC-3") + + # ST Human Resource PC 1 + + assert_web_browser(st_hr_pc_1) + assert_database_client(st_hr_pc_1) + assert_dns_client(st_hr_pc_1) + assert_ntp_client(st_hr_pc_1) + + # ST Human Resource PC 2 + + assert_web_browser(st_hr_pc_2) + assert_database_client(st_hr_pc_2) + assert_dns_client(st_hr_pc_2) + assert_ntp_client(st_hr_pc_2) + + # ST Human Resource PC 3 + + assert_web_browser(st_hr_pc_3) + assert_database_client(st_hr_pc_3) + assert_dns_client(st_hr_pc_3) + assert_ntp_client(st_hr_pc_3) + + +def test_tech_data_software(uc7_network): + """Asserts the database and database storage servers on the some_tech data network are operating as expected.""" + network: Network = uc7_network + st_data_database_server: Server = network.get_node_by_hostname("ST_DATA-PRV-SRV-DB") + st_data_database_storage: Server = network.get_node_by_hostname("ST_DATA-PRV-SRV-STORAGE") + st_proj_a_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-A-PRV-PC-1") + + # Asserting that the database_service is working as expected + database_service: DatabaseService = st_data_database_server.software_manager.software["database-service"] + + assert database_service is not None + assert database_service.operating_state == ServiceOperatingState.RUNNING + assert database_service.health_state_actual == SoftwareHealthState.GOOD + + # Asserting that the database_client can connect to the database + database_client: DatabaseClient = st_proj_a_pc_1.software_manager.software["database-client"] + + assert database_client.server_ip_address is not None + assert database_client.server_ip_address == st_data_database_server.network_interface[1].ip_address + assert database_client.connect() + + # Asserting that the database storage works as expected. + assert database_service.backup_server_ip == st_data_database_storage.network_interface[1].ip_address + assert database_service.backup_database() + + +def test_tech_proj_a_software(uc7_network): + """Asserts that each host on the some_tech project A network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + st_proj_a_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-A-PRV-PC-1") + st_proj_a_pc_2: Computer = network.get_node_by_hostname("ST_PROJ-A-PRV-PC-2") + st_proj_a_pc_3: Computer = network.get_node_by_hostname("ST_PROJ-A-PRV-PC-3") + + # ST Project A - PC 1 + + assert_web_browser(st_proj_a_pc_1) + assert_database_client(st_proj_a_pc_1) + assert_dns_client(st_proj_a_pc_1) + assert_ntp_client(st_proj_a_pc_1) + + # ST Project A - PC 2 + + assert_web_browser(st_proj_a_pc_2) + assert_database_client(st_proj_a_pc_2) + assert_dns_client(st_proj_a_pc_2) + assert_ntp_client(st_proj_a_pc_2) + + # ST Project A - PC 3 + + assert_web_browser(st_proj_a_pc_3) + assert_database_client(st_proj_a_pc_3) + assert_dns_client(st_proj_a_pc_3) + assert_ntp_client(st_proj_a_pc_3) + + +def test_tech_proj_b_software(uc7_network): + """Asserts that each host on the some_tech project A network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + st_proj_b_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-B-PRV-PC-1") + st_proj_b_pc_2: Computer = network.get_node_by_hostname("ST_PROJ-B-PRV-PC-2") + st_proj_b_pc_3: Computer = network.get_node_by_hostname("ST_PROJ-B-PRV-PC-3") + + # ST Project B - PC 1 + + assert_web_browser(st_proj_b_pc_1) + assert_database_client(st_proj_b_pc_1) + assert_dns_client(st_proj_b_pc_1) + assert_ntp_client(st_proj_b_pc_1) + + # ST Project B - PC2 + + assert_web_browser(st_proj_b_pc_2) + assert_database_client(st_proj_b_pc_2) + assert_dns_client(st_proj_b_pc_2) + assert_ntp_client(st_proj_b_pc_2) + + # ST Project B - PC3 + + assert_web_browser(st_proj_b_pc_3) + assert_database_client(st_proj_b_pc_3) + assert_dns_client(st_proj_b_pc_3) + assert_ntp_client(st_proj_b_pc_3) + + +def test_tech_proj_c_software(uc7_network): + """Asserts that each host on the some_tech project A network has the expected services & applications which are operating as expected.""" + network: Network = uc7_network + st_proj_c_pc_1: Computer = network.get_node_by_hostname("ST_PROJ-C-PRV-PC-1") + st_proj_c_pc_2: Computer = network.get_node_by_hostname("ST_PROJ-C-PRV-PC-2") + st_proj_c_pc_3: Computer = network.get_node_by_hostname("ST_PROJ-C-PRV-PC-3") + + # ST Project C - PC 1 + + assert_web_browser(st_proj_c_pc_1) + assert_database_client(st_proj_c_pc_1) + assert_dns_client(st_proj_c_pc_1) + assert_ntp_client(st_proj_c_pc_1) + + # ST Project C - PC2 + + assert_web_browser(st_proj_c_pc_2) + assert_database_client(st_proj_c_pc_2) + assert_dns_client(st_proj_c_pc_2) + assert_ntp_client(st_proj_c_pc_2) + + # ST Project C - PC3 + + assert_web_browser(st_proj_c_pc_3) + assert_database_client(st_proj_c_pc_3) + assert_dns_client(st_proj_c_pc_3) + assert_ntp_client(st_proj_c_pc_3) diff --git a/tests/e2e_integration_tests/threat_actor_profiles/__init__.py b/tests/e2e_integration_tests/threat_actor_profiles/__init__.py new file mode 100644 index 00000000..836b79af --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/__init__.py @@ -0,0 +1 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_abstract_tap.py b/tests/e2e_integration_tests/threat_actor_profiles/test_abstract_tap.py new file mode 100644 index 00000000..cecfa880 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_abstract_tap.py @@ -0,0 +1,142 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain, TAP001 +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain, TAP003 +from primaite.session.environment import PrimaiteGymEnv + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 5 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +ATTACK_AGENT_INDEX = 32 + + +def uc7_tap001_env() -> PrimaiteGymEnv: + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + + env = PrimaiteGymEnv(env_config=cfg) + + return env + + +def uc7_tap003_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with the following settings: + + start_step = Start on Step 1 + frequency = Attack Every 5 Steps + + Each PyTest will define the rest of the TAP & Kill Chain settings via **Kwargs + """ + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", "r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + + if "repeat_kill_chain" in kwargs: + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain"] = kwargs["repeat_kill_chain"] + if "repeat_kill_chain_stages" in kwargs: + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain_stages"] = kwargs[ + "repeat_kill_chain_stages" + ] + if "planning_probability" in kwargs: + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PLANNING"]["probability"] = kwargs[ + "planning_probability" + ] + if "custom_kill_chain" in kwargs: + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"] = kwargs["custom_kill_chain"] + if "starting_nodes" in kwargs: + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["starting_nodes"] = kwargs["starting_nodes"] + if "target_nodes" in kwargs: + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["target_nodes"] = kwargs["target_nodes"] + + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap001_setup(): + """Tests abstract TAP's following methods: + 1. _setup_kill_chain + 2. _setup_agent_kill_chain + 3. _setup_tap_applications + """ + env = uc7_tap001_env() # Using TAP001 for PyTests. + tap: TAP001 = env.game.agents["attacker"] + + # check the kill chain loaded correctly + assert tap.selected_kill_chain is MobileMalwareKillChain + assert tap.selected_kill_chain.FAILED == BaseKillChain.FAILED + assert tap.selected_kill_chain.SUCCEEDED == BaseKillChain.SUCCEEDED + assert tap.selected_kill_chain.NOT_STARTED == BaseKillChain.NOT_STARTED + + if sn := tap.config.agent_settings.default_starting_node: + assert tap.starting_node == sn + else: + assert tap.starting_node in tap.config.agent_settings.starting_nodes + + if ti := tap.config.agent_settings.default_target_ip: + assert tap.target_ip == ti + else: + assert tap.target_ip in tap.config.agent_settings.target_ips + + assert tap.next_execution_timestep == tap.config.agent_settings.start_step + + assert tap.current_host == tap.starting_node + + +def test_abstract_tap_select_start_node(): + """Tests that Abstract TAP's _select_start_node""" + env = uc7_tap003_env(repeat_kill_chain=True, repeat_kill_chain_stages=True) # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + + assert tap.starting_node == "ST_PROJ-A-PRV-PC-1" + assert tap.current_host == tap.starting_node + + +def test_outcome_handler(): + """Tests Abstract Tap's outcome handler concludes the episode when the kill chain fails.""" + env = uc7_tap003_env(repeat_kill_chain=False, repeat_kill_chain_stages=False) # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + tap.current_kill_chain_stage = BaseKillChain.FAILED + # 6 Iterations as the attacker frequency is set to 5. Therefore, after 6 timesteps we expect the agent to realise it's failed the kill chain. + for _ in range(6): + env.step(0) + assert tap.actions_concluded == True + + +def test_abstract_tap_kill_chain_repeat(): + """Tests that the kill chain repeats from the beginning upon failure.""" + env = uc7_tap003_env(repeat_kill_chain=True, repeat_kill_chain_stages=False) # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + for _ in range(15): + env.step(0) # Steps directly to the Access Stage + assert tap.current_kill_chain_stage == InsiderKillChain.ACCESS + tap.current_kill_chain_stage = BaseKillChain.FAILED + for _ in range(5): + env.step(0) # Steps to manipulation - but failure causes the kill chain to restart. + assert tap.actions_concluded == False + assert tap.current_kill_chain_stage == InsiderKillChain.RECONNAISSANCE + + """Tests that kill chain stages repeat when expected""" + env = uc7_tap003_env( + repeat_kill_chain=True, repeat_kill_chain_stages=True, planning_probability=0 + ) # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + tap.current_kill_chain_stage = InsiderKillChain.PLANNING + for _ in range(15): + env.step(0) # Attempts to progress past the PLANNING stage multiple times. + assert tap.actions_concluded == False + assert tap.current_kill_chain_stage == InsiderKillChain.PLANNING diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_kill_chain_methods.py b/tests/e2e_integration_tests/threat_actor_profiles/test_kill_chain_methods.py new file mode 100644 index 00000000..e9a40e45 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_kill_chain_methods.py @@ -0,0 +1,72 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain, TAP001 +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain, TAP003 +from primaite.session.environment import PrimaiteGymEnv + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +ATTACK_AGENT_INDEX = 32 + + +def uc7_tap003_env() -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with a 1 timestep start_step, frequency of 2 and probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def uc7_tap001_env() -> PrimaiteGymEnv: + """Setup the UC7 TAP001 Game with the start_step & frequency set to 1 & 2 respectively. Probabilities are set to 1""" + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap003_insider_kill_chain_load(): + """Tests that tap003's insider kill chain is successfully loaded into the tap.selected_kill_chain attribute.""" + env = uc7_tap003_env() # Using TAP003 for PyTests. + tap: TAP003 = env.game.agents["attacker"] + # Asserting that the Base Kill Chain intEnum stages are loaded + kill_chain_enums = [enums for enums in tap.selected_kill_chain] + assert BaseKillChain.FAILED in kill_chain_enums + assert BaseKillChain.SUCCEEDED in kill_chain_enums + assert BaseKillChain.NOT_STARTED in kill_chain_enums + # Asserting that the Insider Kill Chain intenum stages are loaded. + assert len(InsiderKillChain.__members__) == len(tap.selected_kill_chain.__members__) + + +def test_tap001_mobile_malware_kill_chain_load(): + """Tests that tap001's mobile malware is successfully loaded into the tap.selected_kill_chain attribute.""" + env = uc7_tap001_env() # Using TAP003 for PyTests. + tap: TAP001 = env.game.agents["attacker"] + # Asserting that the Base Kill Chain intEnum stages are loaded. + kill_chain_enums = [enums for enums in tap.selected_kill_chain] + assert BaseKillChain.FAILED in kill_chain_enums + assert BaseKillChain.SUCCEEDED in kill_chain_enums + assert BaseKillChain.NOT_STARTED in kill_chain_enums + # Asserting that the Insider Kill Chain intEnum stages are loaded. + assert len(MobileMalwareKillChain.__members__) == len(tap.selected_kill_chain.__members__) diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_repeat.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_repeat.py new file mode 100644 index 00000000..94832ed0 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_repeat.py @@ -0,0 +1,116 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain, TAP001 +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain, TAP003 +from primaite.session.environment import PrimaiteGymEnv + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +ATTACK_AGENT_INDEX = 32 + + +def uc7_tap001_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 tap001 game with a 1 timestep start_step, frequency of 2 and probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain"] = kwargs["repeat_kill_chain"] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain_stages"] = kwargs[ + "repeat_kill_chain_stages" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PROPAGATE"]["probability"] = kwargs[ + "propagate_probability" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PAYLOAD"]["probability"] = kwargs[ + "payload_probability" + ] + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap001_repeating_kill_chain(): + """Tests to check that tap001 repeats it's kill chain after success""" + env = uc7_tap001_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + payload_probability=1, + propagate_probability=1, + ) + tap001: TAP001 = env.game.agents["attacker"] + # Looping for 50 timesteps - As the agent is set to execute an action every 2 timesteps + # This is the equivalent of the agent taking 25 actions. + for _ in range(50): # This for loop should never actually fully complete. + if tap001.current_kill_chain_stage == BaseKillChain.SUCCEEDED: + break + env.step(0) + + # Catches if the above for loop fully completes. + # This test uses a probability of 1 for all stages and a frequency of 2 timesteps + # Thus the for loop above should never complete it's full 50 iterations. + # If this occurs then there is an error somewhere in either: + # 1. The TAP Logic + # 2. Failing Agent Actions are causing the TAP to fail. (See tap_return_handler). + if tap001.current_kill_chain_stage != BaseKillChain.SUCCEEDED: + pytest.fail("Attacker Never Reached SUCCEEDED - Please evaluate current TAP Logic.") + + # Stepping four timesteps (2 TAP001 steps) for the succeeded logic to kick in: + env.step(0) + env.step(0) + env.step(0) + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.DOWNLOAD.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + + +def test_tap001_repeating_kill_chain_stages(): + """Tests to check that tap001 repeats it's kill chain after failing a kill chain stage.""" + env = uc7_tap001_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + payload_probability=1, + propagate_probability=0, + # Probability 0 = Will never be able to perform the access stage and progress to Manipulation. + ) + tap001: TAP001 = env.game.agents["attacker"] + env.step(0) # Skipping not started + env.step(0) # Successful on the first stage + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.DOWNLOAD.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + env.step(0) # Successful progression to the second stage + env.step(0) + env.step(0) + env.step(0) + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.ACTIVATE.name + env.step(0) # Successful progression to the third stage + env.step(0) + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.ACTIVATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + env.step(0) # Successful progression to the Fourth stage + env.step(0) + env.step(0) + env.step(0) + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + env.step(0) # FAILURE -- Unsuccessful progression to the Fourth stage + env.step(0) + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + assert tap001.current_stage_progress == KillChainStageProgress.PENDING diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_stages.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_stages.py new file mode 100644 index 00000000..851910d1 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_kill_chain_stages.py @@ -0,0 +1,215 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain, TAP001 +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain, TAP003 +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.system.applications.red_applications.c2.c2_beacon import C2Beacon +from primaite.simulator.system.services.database.database_service import DatabaseService + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +REPEAT_KILL_CHAIN = False # Should the TAP repeat the kill chain after success/failure? +REPEAT_KILL_CHAIN_STAGES = False # Should the TAP restart from it's previous stage on failure? +KILL_CHAIN_PROBABILITY = 1 # Blank probability for agent 'success' +DATA_EXFIL = True # Data exfiltration on the payload stage is enabled. +ATTACK_AGENT_INDEX = 32 + + +def uc7_tap001_env() -> PrimaiteGymEnv: + """Setups the UC7 tap001 Game with the start_step & frequency set to 1 with probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain"] = REPEAT_KILL_CHAIN_STAGES + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain_stages"] = REPEAT_KILL_CHAIN_STAGES + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PAYLOAD"][ + "probability" + ] = KILL_CHAIN_PROBABILITY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PROPAGATE"][ + "probability" + ] = KILL_CHAIN_PROBABILITY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PAYLOAD"]["exfiltrate"] = DATA_EXFIL + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap001_kill_chain_stage_DOWNLOAD(): + """Tests that the DOWNLOAD Mobile Malware step works as expected and the expected impacts are made in the simulation.""" + + # Instantiating the relevant simulation/game objects: + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + assert tap001.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + # Frequency is set to two steps so we need to step through the environment a couple of times + # In order for TAP001 to move onto the next kill chain stage. + env.step(0) + env.step(0) + + env.step(0) + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.DOWNLOAD.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + assert tap001.current_stage_progress == KillChainStageProgress.IN_PROGRESS + + # Creating the "downloads" folder + env.step(0) + env.step(0) + + assert starting_host.software_manager.file_system.get_folder(folder_name="downloads") + assert starting_host.software_manager.file_system.get_file(folder_name="downloads", file_name="malware_dropper.ps1") + + # Testing that the num_file_increase works + + assert starting_host.file_system.num_file_creations == 1 + + +def test_tap001_kill_chain_stage_INSTALL(): + """Tests that the INSTALL Mobile Malware step works as expected and the expected impacts are made in the simulation.""" + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + # The tap001's Starting Client: + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + + # Skipping directly to the activate stage + for _ in range(6): + env.step(0) + + # Testing that tap001 Enters into the expected kill chain stages + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.INSTALL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.ACTIVATE.name + + env.step(0) # Allows the agent action to resolve. + env.step(0) + + ransomware_dropper_file = starting_host.software_manager.file_system.get_file( + folder_name="downloads", file_name="malware_dropper.ps1" + ) + assert ransomware_dropper_file.num_access == 1 + + +def test_tap001_kill_chain_stage_ACTIVATE(): + """Tests that the ACTIVATE Mobile Malware step works as expected and the current impacts are made in the simulation.""" + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + # The tap001's Starting Client: + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + + # Skipping directly to the activate stage + for _ in range(8): + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.ACTIVATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + + # Installing ransomware-script Application + env.step(0) + env.step(0) + + # Installing NMAP Application + env.step(0) + env.step(0) + + # These asserts will fail if the applications are not present in the software_manager + assert starting_host.software_manager.software["ransomware-script"] + assert starting_host.software_manager.software["nmap"] + + +def test_tap001_kill_chain_stage_PROPAGATE(): + """Tests that the ACTIVATE Mobile Malware step works as expected and the current impacts are made in the simulation.""" + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + + for _ in range(12): + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + + # Specific Stage by Stage Propagate Testing is done in test_tap001_propagate. + fail_safe_var = 0 + while tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE: + env.step(0) + assert tap001.current_stage_progress == KillChainStageProgress.IN_PROGRESS + fail_safe_var += 1 + if fail_safe_var == 100: + pytest.fail("Fail Safe Variable was hit! -- Propagate step is running indefinitely") + + +def test_tap001_kill_chain_stage_COMMAND_AND_CONTROL(): + """Tests that the Command And Control Mobile Malware step works as expected and the current impacts are made in the simulation.""" + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + fail_safe_var = 0 + + for _ in range(28): + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PAYLOAD.name + + while tap001.current_kill_chain_stage == MobileMalwareKillChain.COMMAND_AND_CONTROL: + env.step(0) + fail_safe_var += 1 + env.game.simulation.network.airspace.show() + if fail_safe_var == 100: + pytest.fail(reason="Fail Safe Variable was hit! -- Propagate step is running indefinitely") + + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + + c2_beacon: C2Beacon = starting_host.software_manager.software["c2-beacon"] + + assert c2_beacon.c2_connection_active is True + + +def test_tap001_kill_chain_stage_PAYLOAD(): + """Tests that the PAYLOAD Mobile Malware step works as expected and the current impacts are made in the simulation.""" + + env = uc7_tap001_env() + tap001: TAP001 = env.game.agents["attacker"] + + # The tap001's Target Database + target_host = env.game.simulation.network.get_node_by_hostname("ST_DATA-PRV-SRV-DB") + db_server_service: DatabaseService = target_host.software_manager.software.get("database-service") + + # Green agent status requests are tested within the ransomware application tests. + # See test_ransomware_disrupts_green_agent_connection for further reference. + assert db_server_service.db_file.health_status is FileSystemItemHealthStatus.GOOD + + fail_safe_var = 0 + while tap001.current_kill_chain_stage != MobileMalwareKillChain.PAYLOAD: + env.step(0) + fail_safe_var += 1 + if fail_safe_var == 100: + pytest.fail(reason="Fail Safe Variable was hit! -- a step is running indefinitely") + + for _ in range(12): + env.step(0) + + assert db_server_service.db_file.health_status is FileSystemItemHealthStatus.CORRUPT + + # Asserting we've managed to the database.db file onto the starting node & server + starting_host = env.game.simulation.network.get_node_by_hostname(tap001.starting_node) + c2_host = env.game.simulation.network.get_node_by_hostname(tap001.c2_settings["c2_server"]) + + assert starting_host.file_system.access_file(folder_name="exfiltration_folder", file_name="database.db") + assert c2_host.file_system.access_file(folder_name="exfiltration_folder", file_name="database.db") diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_propagate_stage.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_propagate_stage.py new file mode 100644 index 00000000..24fbaa23 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap001_propagate_stage.py @@ -0,0 +1,141 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain, TAP001 +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain, TAP003 +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.file_system.file_system_item_abc import FileSystemItemHealthStatus +from primaite.simulator.system.applications.red_applications.c2.c2_beacon import C2Beacon +from primaite.simulator.system.services.database.database_service import DatabaseService + +# Defining generic tap constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +REPEAT_KILL_CHAIN = False # Should the TAP repeat the kill chain after success/failure? +REPEAT_KILL_CHAIN_STAGES = False # Should the TAP restart from it's previous stage on failure? +KILL_CHAIN_PROBABILITY = 1 # Blank probability for agent 'success's. +ATTACK_AGENT_INDEX = 32 + + +def uc7_tap001_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 tap001 Game with the start_step & frequency set to 1 with probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + agent_cfg = cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"] + agent_cfg["start_step"] = START_STEP + agent_cfg["frequency"] = FREQUENCY + agent_cfg["variance"] = VARIANCE + agent_cfg["repeat_kill_chain"] = REPEAT_KILL_CHAIN_STAGES + agent_cfg["repeat_kill_chain_stages"] = REPEAT_KILL_CHAIN_STAGES + agent_cfg["kill_chain"]["PAYLOAD"]["probability"] = KILL_CHAIN_PROBABILITY + agent_cfg["kill_chain"]["PROPAGATE"]["probability"] = KILL_CHAIN_PROBABILITY + agent_cfg["kill_chain"]["PROPAGATE"]["scan_attempts"] = kwargs["scan_attempts"] + agent_cfg["kill_chain"]["PAYLOAD"]["payload"] = kwargs["payload"] + agent_cfg["kill_chain"]["PROPAGATE"]["network_addresses"] = kwargs["network_addresses"] + if "repeat_scan" in kwargs: + agent_cfg["kill_chain"]["PROPAGATE"]["repeat_scan"] = kwargs["repeat_scan"] + if "starting_nodes" in kwargs: + agent_cfg["starting_nodes"] = kwargs["starting_nodes"] + agent_cfg["default_starting_node"] = kwargs["starting_nodes"][0] + if "target_ips" in kwargs: + agent_cfg["target_ips"] = kwargs["target_ips"] + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap001_kill_chain_stage_PROPAGATE_default(): + """Tests that the PROPAGATE Mobile Malware step works as expected and the current impacts are made in the simulation.""" + payload = "ENCRYPT" + scan_attempts = 10 + network_addresses = [ + "192.168.230.0/29", + "192.168.10.0/26", + "192.168.20.0/30", + "192.168.240.0/29", + "192.168.220.0/29", + ] + env = uc7_tap001_env(payload=payload, scan_attempts=scan_attempts, network_addresses=network_addresses) + tap001: TAP001 = env.game.agents["attacker"] + + # First Kill Chain Stages + for _ in range(12): + env.step(0) + + # Assert that we're about to enter into the propagate stage. + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + + # Move into the propagate stage. + while tap001.current_kill_chain_stage == MobileMalwareKillChain.PROPAGATE: + env.step(0) + + # Assert that we've successfully moved into the command and control stage. + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PAYLOAD.name + + +def test_tap001_kill_chain_stage_PROPAGATE_different_starting_node(): + """Tests that the PROPAGATE Mobile Malware step works as expected and the current impacts are made in the simulation from a different starting node.""" + payload = "ENCRYPT" + scan_attempts = 10 + network_addresses = [ + "192.168.230.0/29", + "192.168.10.0/26", + "192.168.20.0/30", + "192.168.240.0/29", + "192.168.220.0/29", + ] + starting_nodes = ["ST_PROJ-B-PRV-PC-2", "ST_PROJ-C-PRV-PC-3"] + + env = uc7_tap001_env( + payload=payload, scan_attempts=scan_attempts, network_addresses=network_addresses, starting_nodes=starting_nodes + ) + + tap001: TAP001 = env.game.agents["attacker"] + + for _ in range(12): + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.PROPAGATE.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + + # Specific Stage by Stage Propagate Testing is done in test_tap001_propagate. + while tap001.current_kill_chain_stage == MobileMalwareKillChain.PROPAGATE: + env.step(0) + + assert tap001.current_kill_chain_stage.name == MobileMalwareKillChain.COMMAND_AND_CONTROL.name + assert tap001.next_kill_chain_stage.name == MobileMalwareKillChain.PAYLOAD.name + + +def test_tap001_kill_chain_stage_PROPAGATE_repeat_scan(): + """Tests that the PROPAGATE Mobile Malware step will fail when the target is unable to be located.""" + payload = "ENCRYPT" + scan_attempts = 20 + repeat_scan = True + network_addresses = ["192.168.1.0/24", "192.168.0.0/28", "100.64.0.0/30", "172.168.0.0/28"] + env = uc7_tap001_env( + payload=payload, scan_attempts=scan_attempts, network_addresses=network_addresses, repeat_scan=repeat_scan + ) + for _ in range(12): + env.step(0) + + tap001: TAP001 = env.game.agents["attacker"] + + while tap001.current_kill_chain_stage == MobileMalwareKillChain.PROPAGATE: + env.step(0) + + # As the given network_address does not contain the target, we should fail because the maximum amount of scan attempts has been reached + assert tap001.scans_complete == scan_attempts + assert tap001.current_kill_chain_stage == MobileMalwareKillChain.FAILED diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_repeat.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_repeat.py new file mode 100644 index 00000000..7df846b1 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_repeat.py @@ -0,0 +1,108 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain, TAP001 +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain, TAP003 +from primaite.session.environment import PrimaiteGymEnv + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +ATTACK_AGENT_INDEX = 32 + + +def uc7_tap003_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with a 1 timestep start_step, frequency of 2 and probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain"] = kwargs["repeat_kill_chain"] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain_stages"] = kwargs[ + "repeat_kill_chain_stages" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["MANIPULATION"]["probability"] = kwargs[ + "manipulation_probability" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["ACCESS"]["probability"] = kwargs[ + "access_probability" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PLANNING"]["probability"] = kwargs[ + "planning_probability" + ] + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap003_repeating_kill_chain(): + """Tests to check that TAP003 repeats it's kill chain after success""" + env = uc7_tap003_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + manipulation_probability=1, + access_probability=1, + planning_probability=1, + ) + tap003: TAP003 = env.game.agents["attacker"] + for _ in range(40): # This for loop should never actually fully complete. + if tap003.current_kill_chain_stage == BaseKillChain.SUCCEEDED: + break + env.step(0) + + # Catches if the above for loop fully completes. + # This test uses a probability of 1 for all stages and a variance of 2 timesteps + # Thus the for loop above should never fail. + # If this occurs then there is an error somewhere in either: + # 1. The TAP Logic + # 2. Failing Agent Actions are causing the TAP to fail. (See tap_return_handler). + if tap003.current_kill_chain_stage != BaseKillChain.SUCCEEDED: + pytest.fail("Attacker Never Reached SUCCEEDED - Please evaluate current TAP Logic.") + + # Stepping twice for the succeeded logic to kick in: + env.step(0) + env.step(0) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + +def test_tap003_repeating_kill_chain_stages(): + """Tests to check that TAP003 repeats it's kill chain after failing a kill chain stage.""" + env = uc7_tap003_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + manipulation_probability=1, + # access_probability 0 = Will never be able to perform the access stage and progress to Manipulation. + access_probability=0, + planning_probability=1, + ) + tap003: TAP003 = env.game.agents["attacker"] + env.step(0) # Skipping not started + env.step(0) # Successful on the first stage + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + env.step(0) # Successful progression to the second stage + env.step(0) + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + env.step(0) # Successfully moved onto access. + env.step(0) + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + env.step(0) # Failure to progress past the third stage. + env.step(0) + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_stages.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_stages.py new file mode 100644 index 00000000..bea4ed26 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_kill_chain_stages.py @@ -0,0 +1,218 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain, TAP003 +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.network.hardware.nodes.network.firewall import Firewall +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router + +# Defining constants. + +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +REPEAT_KILL_CHAIN = False # Should the TAP repeat the kill chain after success/failure? +REPEAT_KILL_CHAIN_STAGES = False # Should the TAP restart from it's previous stage on failure? +KILL_CHAIN_PROBABILITY = 1 # Blank probability for agent 'success' +ATTACK_AGENT_INDEX = 32 + + +def uc7_tap003_env() -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with a 1 timestep start_step, frequency of 2 and probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["io_settings"]["save_agent_logs"] = True + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain"] = REPEAT_KILL_CHAIN_STAGES + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain_stages"] = REPEAT_KILL_CHAIN_STAGES + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["MANIPULATION"][ + "probability" + ] = KILL_CHAIN_PROBABILITY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["ACCESS"][ + "probability" + ] = KILL_CHAIN_PROBABILITY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PLANNING"][ + "probability" + ] = KILL_CHAIN_PROBABILITY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["EXPLOIT"][ + "probability" + ] = KILL_CHAIN_PROBABILITY + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def environment_step(i: int, env: PrimaiteGymEnv) -> PrimaiteGymEnv: + """Carries out i (given parameter) steps in the environment..""" + for x in range(i): + env.step(0) + return env + + +def test_tap003_kill_chain_stage_reconnaissance(): + """Tests the successful/failed handlers in the reconnaissance stage in the Insider Kill Chain InsiderKillChain""" + + # Instantiating the relevant simulation/game objects: + env = uc7_tap003_env() + tap003: TAP003 = env.game.agents["attacker"] + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + # Frequency is set to two steps + env = environment_step(i=2, env=env) + + # Testing that TAP003 Enters into the expected kill chain stages + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + + +def test_tap003_kill_chain_stage_planning(): + """Tests the successful/failed handlers in the planning stage in the Insider Kill Chain (TAP003)""" + env = uc7_tap003_env() + tap003: TAP003 = env.game.agents["attacker"] + + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + env = environment_step(i=2, env=env) + + # Testing that TAP003 Enters into the expected kill chain stages + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + + env = environment_step(i=2, env=env) + + # Testing that the stage successful - TAP003 has loaded it's starting network knowledge into it's network knowledge. + + # At this point TAP003 will parse it's starting network knowledge config into it's a private attribute (`network_knowledge`) + assert ( + tap003.network_knowledge["credentials"] + == tap003.config.agent_settings.kill_chain.PLANNING.starting_network_knowledge["credentials"] + ) + + +def test_tap003_kill_chain_stage_access(): + """Tests the successful/failed handlers in the access stage in the InsiderKillChain""" + env = uc7_tap003_env() + tap003: TAP003 = env.game.agents["attacker"] + + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + + +def test_tap003_kill_chain_stage_manipulation(): + """Tests the successful/failed handlers in the manipulation stage in the InsiderKillChain""" + env = uc7_tap003_env() + tap003: TAP003 = env.game.agents["attacker"] + + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + + # Testing that the stage successfully impacted the simulation - Accounts Altered + env = environment_step(i=5, env=env) + st_intra_prv_rt_dr_1: Router = env.game.simulation.network.get_node_by_hostname("ST_INTRA-PRV-RT-DR-1") + assert tap003.current_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + assert st_intra_prv_rt_dr_1.user_manager.admins["admin"].password == "red_pass" + + env = environment_step(i=5, env=env) + st_intra_prv_rt_cr: Router = env.game.simulation.network.get_node_by_hostname("ST_INTRA-PRV-RT-CR") + assert tap003.current_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + assert st_intra_prv_rt_cr.user_manager.admins["admin"].password == "red_pass" + + env = environment_step(i=5, env=env) + rem_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname("REM-PUB-RT-DR") + assert rem_pub_rt_dr.user_manager.admins["admin"].password == "red_pass" + + +def test_tap003_kill_chain_stage_exploit(): + """Tests the successful/failed handlers in the exploit stage in the InsiderKillChain""" + + env = uc7_tap003_env() + tap003: TAP003 = env.game.agents["attacker"] + # The TAP003's Target Router/Firewall + st_intra_prv_rt_dr_1: Router = env.game.simulation.network.get_node_by_hostname("ST_INTRA-PRV-RT-DR-1") + st_intra_prv_rt_cr: Router = env.game.simulation.network.get_node_by_hostname("ST_INTRA-PRV-RT-CR") + rem_pub_rt_dr: Router = env.game.simulation.network.get_node_by_hostname("REM-PUB-RT-DR") + + assert tap003.current_kill_chain_stage == BaseKillChain.NOT_STARTED + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.RECONNAISSANCE.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.PLANNING.name + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.PLANNING.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.ACCESS.name + + env = environment_step(i=2, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.ACCESS.name + assert tap003.next_kill_chain_stage.name == InsiderKillChain.MANIPULATION.name + + env = environment_step(i=16, env=env) + + assert tap003.current_kill_chain_stage.name == InsiderKillChain.EXPLOIT.name + + # Testing that the stage successfully impacted the simulation - Malicious ACL Added: + for _ in range(14): + env.step(0) + + # Tests that the ACL has been added and that the action is deny. + st_intra_prv_rt_dr_1_acl_list = st_intra_prv_rt_dr_1.acl + assert st_intra_prv_rt_dr_1_acl_list.acl[1].action != None + assert st_intra_prv_rt_dr_1_acl_list.acl[1].action == ACLAction.DENY + + st_intra_prv_rt_cr_acl_list = st_intra_prv_rt_cr.acl + assert st_intra_prv_rt_cr_acl_list.acl[1].action != None + assert st_intra_prv_rt_cr_acl_list.acl[1].action == ACLAction.DENY + + rem_pub_rt_dr_acl_list = rem_pub_rt_dr.acl + assert rem_pub_rt_dr_acl_list.acl[1].action != None + assert rem_pub_rt_dr_acl_list.acl[1].action == ACLAction.DENY diff --git a/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_multiple_rules.py b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_multiple_rules.py new file mode 100644 index 00000000..0db318a7 --- /dev/null +++ b/tests/e2e_integration_tests/threat_actor_profiles/test_tap003_multiple_rules.py @@ -0,0 +1,203 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +from typing import Protocol + +import pytest +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.abstract_tap import ( + AbstractTAP, + BaseKillChain, + KillChainOptions, + KillChainStageOptions, + KillChainStageProgress, +) +from primaite.game.agent.scripted_agents.TAP001 import MobileMalwareKillChain, TAP001 +from primaite.game.agent.scripted_agents.TAP003 import InsiderKillChain, TAP003 +from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator.network.hardware.nodes.network.router import ACLAction, Router +from primaite.utils.validation.ip_protocol import PROTOCOL_LOOKUP +from primaite.utils.validation.ipv4_address import IPV4Address +from primaite.utils.validation.port import PORT_LOOKUP + +# Defining constants. +ATTACK_AGENT_INDEX = 32 +START_STEP = 1 # The starting step of the agent. +FREQUENCY = 2 # The frequency of kill chain stage progression (E.g it's next attempt at "attacking"). +VARIANCE = 0 # The timestep variance between kill chain progression (E.g Next timestep = Frequency +/- variance) +REPEAT_KILL_CHAIN = False # Should the TAP repeat the kill chain after success/failure? +REPEAT_KILL_CHAIN_STAGES = False # Should the TAP restart from it's previous stage on failure? +KILL_CHAIN_PROBABILITY = 1 # Blank probability for agent 'success' +RULES = [ + { + "target_router": "ST_INTRA-PRV-RT-DR-1", + "position": 1, + "permission": "DENY", + "src_ip": "192.168.220.3", + "src_wildcard": "NONE", + "dst_ip": "192.168.220.3", + "dst_wildcard": "NONE", + "src_port": "ALL", + "dst_port": "ALL", + "protocol_name": "ALL", + }, + { + "target_router": "ST_INTRA-PRV-RT-DR-2", + "position": 5, + "permission": "DENY", + "src_ip": "192.168.220.3", + "src_wildcard": "NONE", + "dst_ip": "ALL", + "dst_wildcard": "NONE", + "src_port": "ALL", + "dst_port": "ALL", + "protocol_name": "ALL", + }, + { + "target_router": "ST_INTRA-PRV-RT-CR", + "position": 6, + "permission": "PERMIT", + "src_ip": "192.168.220.3", + "src_wildcard": "NONE", + "dst_ip": "ALL", + "dst_wildcard": "NONE", + "src_port": "ALL", + "dst_port": "ALL", + "protocol_name": "ALL", + }, + { + "target_router": "REM-PUB-RT-DR", + "position": 3, + "permission": "PERMIT", + "src_ip": "192.168.220.3", + "src_wildcard": "0.0.0.1", + "dst_ip": "192.168.220.3", + "dst_wildcard": "0.0.0.1", + "src_port": "FTP", + "dst_port": "FTP", + "protocol_name": "TCP", + }, + # +] + + +def uc7_tap003_env(**kwargs) -> PrimaiteGymEnv: + """Setups the UC7 TAP003 Game with a 1 timestep start_step, frequency of 2 and probabilities set to 1 as well""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["io_settings"]["save_sys_logs"] = False + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["start_step"] = START_STEP + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["frequency"] = FREQUENCY + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["variance"] = VARIANCE + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain"] = kwargs["repeat_kill_chain"] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["repeat_kill_chain_stages"] = kwargs[ + "repeat_kill_chain_stages" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["MANIPULATION"]["probability"] = kwargs[ + "manipulation_probability" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["ACCESS"]["probability"] = kwargs[ + "access_probability" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PLANNING"]["probability"] = kwargs[ + "planning_probability" + ] + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["EXPLOIT"]["malicious_acls"] = RULES + # Adding the new test target to TAP003's starting knowledge: + new_target_dict = { + "ST_INTRA-PRV-RT-DR-2": { + "ip_address": "192.168.170.2", + "username": "admin", + "password": "admin", + } + } + new_target_manipulation = { + "host": "ST_INTRA-PRV-RT-DR-2", + "ip_address": "192.168.170.2", + "action": "change_password", + "username": "admin", + "new_password": "red_pass", + } + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PLANNING"]["starting_network_knowledge"][ + "credentials" + ].update(new_target_dict) + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["MANIPULATION"]["account_changes"].append( + new_target_manipulation + ) + env = PrimaiteGymEnv(env_config=cfg) + return env + + +def test_tap003_cycling_rules(): + """Tests to check that TAP003 repeats it's kill chain after success""" + + env = uc7_tap003_env( + repeat_kill_chain=True, + repeat_kill_chain_stages=True, + manipulation_probability=1, + access_probability=1, + planning_probability=1, + ) + tap003: TAP003 = env.game.agents["attacker"] + + def wait_until_attack(): + # 120 environment steps to ensure that TAP003 reaches manipulate. + # If this loop finishes 120 iterations before the test finishes then TAP003 is struggling to + # reach or finish the manipulation kill chain stage correctly. + for _ in range(120): + # check if the agent has executed and therefore moved onto the next rule index + env.step(0) + if tap003.history[-1].action == "node-send-remote-command": + if tap003.history[-1].parameters["command"][0] == "acl": + return + pytest.fail("While testing the cycling of TAP003 rules, the agent unexpectedly didn't execute its attack.") + + wait_until_attack() + target_node: Router = env.game.simulation.network.get_node_by_hostname("ST_INTRA-PRV-RT-DR-1") + assert (rule_0 := target_node.acl.acl[1]) is not None + assert rule_0.action == ACLAction.DENY + assert rule_0.protocol == None + assert rule_0.src_ip_address == IPV4Address("192.168.220.3") + assert rule_0.src_wildcard_mask == None + assert rule_0.dst_ip_address == IPV4Address("192.168.220.3") + assert rule_0.dst_wildcard_mask == None + assert rule_0.src_port == None + assert rule_0.dst_port == None + + target_node: Router = env.game.simulation.network.get_node_by_hostname("ST_INTRA-PRV-RT-DR-2") + wait_until_attack() + assert (rule_1 := target_node.acl.acl[5]) is not None + assert rule_1.action == ACLAction.DENY + assert rule_1.protocol == None + assert rule_1.src_ip_address == IPV4Address("192.168.220.3") + assert rule_1.src_wildcard_mask == None + assert rule_1.dst_ip_address == None + assert rule_1.dst_wildcard_mask == None + assert rule_1.src_port == None + assert rule_1.dst_port == None + + wait_until_attack() + target_node: Router = env.game.simulation.network.get_node_by_hostname("ST_INTRA-PRV-RT-CR") + assert (rule_2 := target_node.acl.acl[6]) is not None + assert rule_2.action == ACLAction.PERMIT + assert rule_2.protocol == None + assert rule_2.src_ip_address == IPV4Address("192.168.220.3") + assert rule_2.src_wildcard_mask == None # default + assert rule_2.dst_ip_address == None + assert rule_2.dst_wildcard_mask == None # default + assert rule_2.src_port == None + assert rule_2.dst_port == None + + wait_until_attack() + target_node: Router = env.game.simulation.network.get_node_by_hostname("REM-PUB-RT-DR") + assert (rule_3 := target_node.acl.acl[3]) is not None + assert rule_3.action == ACLAction.PERMIT + assert rule_3.protocol == PROTOCOL_LOOKUP["TCP"] + assert rule_3.src_ip_address == IPV4Address("192.168.220.3") + assert rule_3.src_wildcard_mask == IPV4Address("0.0.0.1") + assert rule_3.dst_ip_address == IPV4Address("192.168.220.3") + assert rule_3.dst_wildcard_mask == IPV4Address("0.0.0.1") + assert rule_3.src_port == PORT_LOOKUP["FTP"] + assert rule_3.dst_port == PORT_LOOKUP["FTP"] + + # If we've gotten this fair then we can pass the test :) diff --git a/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/__init__.py b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/__init__.py new file mode 100644 index 00000000..836b79af --- /dev/null +++ b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/__init__.py @@ -0,0 +1 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK diff --git a/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_kill_chain_settings.py b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_kill_chain_settings.py new file mode 100644 index 00000000..59fd9d86 --- /dev/null +++ b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_kill_chain_settings.py @@ -0,0 +1,25 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +from pathlib import Path +from typing import Union + +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.TAP003 import TAP003 +from primaite.game.game import PrimaiteGame + +ATTACK_AGENT_INDEX = 32 + + +def test_tap003_kill_chain_settings_load_config(): + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["MANIPULATION"]["probability"] = 0.5 + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["ACCESS"]["probability"] = 0.5 + cfg["agents"][ATTACK_AGENT_INDEX]["agent_settings"]["kill_chain"]["PLANNING"]["probability"] = 0.5 + game = PrimaiteGame.from_config(cfg) + tap: TAP003 = game.agents["attacker"] + kill_chain = tap.config.agent_settings.kill_chain + assert kill_chain.MANIPULATION.probability == 0.5 + assert kill_chain.ACCESS.probability == 0.5 + assert kill_chain.PLANNING.probability == 0.5 diff --git a/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_threat_actor_profile_settings.py b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_threat_actor_profile_settings.py new file mode 100644 index 00000000..03216865 --- /dev/null +++ b/tests/integration_tests/configuration_file_parsing/threat_actor_profile_settings/test_threat_actor_profile_settings.py @@ -0,0 +1,35 @@ +# © Crown-owned copyright 2025, Defence Science and Technology Laboratory UK +from pathlib import Path +from typing import Union + +import yaml + +from primaite.config.load import _EXAMPLE_CFG +from primaite.game.agent.scripted_agents.TAP003 import TAP003 +from primaite.game.game import PrimaiteGame + + +def test_threat_actor_profile_load_config(): + """Test to check that threat actor profiles are able to be loaded.""" + with open(_EXAMPLE_CFG / "uc7_config_tap003.yaml", mode="r") as uc7_config: + cfg = yaml.safe_load(uc7_config) + + game = PrimaiteGame.from_config(cfg) + # tap003 is found and loaded TODO: Once tuple digestion is implemented, change to hardcoded 'tap003' test. + assert "attacker" in game.agents + assert isinstance(game.agents["attacker"], TAP003) + agent: TAP003 = game.agents["attacker"] + assert agent.config.agent_settings.start_step == 1 + assert agent.config.agent_settings.frequency == 3 + assert agent.config.agent_settings.variance == 0 + assert not agent.config.agent_settings.repeat_kill_chain + assert agent.config.agent_settings.repeat_kill_chain_stages + assert agent.config.agent_settings.default_starting_node == "ST_PROJ-A-PRV-PC-1" + assert not agent.config.agent_settings.starting_nodes + assert agent.config.agent_settings.kill_chain.PLANNING.probability == 1 + assert len(agent.config.agent_settings.kill_chain.PLANNING.starting_network_knowledge["credentials"]) == 6 + assert agent.config.agent_settings.kill_chain.ACCESS.probability == 1 + assert agent.config.agent_settings.kill_chain.MANIPULATION.probability == 1 + assert len(agent.config.agent_settings.kill_chain.MANIPULATION.account_changes) == 3 + assert agent.config.agent_settings.kill_chain.EXPLOIT.probability == 1 + assert len(agent.config.agent_settings.kill_chain.EXPLOIT.malicious_acls) == 3 diff --git a/tests/integration_tests/game_layer/observations/test_acl_observations.py b/tests/integration_tests/game_layer/observations/test_acl_observations.py index 0a633b2d..c70c454d 100644 --- a/tests/integration_tests/game_layer/observations/test_acl_observations.py +++ b/tests/integration_tests/game_layer/observations/test_acl_observations.py @@ -47,7 +47,7 @@ def test_acl_observations(simulation): observation_space = acl_obs.observe(simulation.describe_state()) assert observation_space.get(1) is not None rule_obs = observation_space.get(1) # this is the ACL Rule added to allow NTP - assert rule_obs.get("position") == 0 # rule was put at position 1 (0 because counting from 1 instead of 1) + assert rule_obs.get("position") == 1 # rule was put at position 1 assert rule_obs.get("permission") == 1 # permit = 1 deny = 2 assert rule_obs.get("source_ip_id") == 1 # applies to all source nodes assert rule_obs.get("dest_ip_id") == 1 # applies to all destination nodes @@ -60,7 +60,7 @@ def test_acl_observations(simulation): observation_space = acl_obs.observe(simulation.describe_state()) assert observation_space.get(1) is not None rule_obs = observation_space.get(1) # this is the ACL Rule added to allow NTP - assert rule_obs.get("position") == 0 + assert rule_obs.get("position") == 1 assert rule_obs.get("permission") == 0 assert rule_obs.get("source_ip_id") == 0 assert rule_obs.get("dest_ip_id") == 0 diff --git a/tests/integration_tests/game_layer/observations/test_firewall_observation.py b/tests/integration_tests/game_layer/observations/test_firewall_observation.py index 874fa49e..431fff91 100644 --- a/tests/integration_tests/game_layer/observations/test_firewall_observation.py +++ b/tests/integration_tests/game_layer/observations/test_firewall_observation.py @@ -11,15 +11,15 @@ from primaite.utils.validation.port import PORT_LOOKUP def check_default_rules(acl_obs): assert len(acl_obs) == 7 - assert all(acl_obs[i]["position"] == i - 1 for i in range(1, 8)) - assert all(acl_obs[i]["permission"] == 0 for i in range(1, 8)) - assert all(acl_obs[i]["source_ip_id"] == 0 for i in range(1, 8)) - assert all(acl_obs[i]["source_wildcard_id"] == 0 for i in range(1, 8)) - assert all(acl_obs[i]["source_port_id"] == 0 for i in range(1, 8)) - assert all(acl_obs[i]["dest_ip_id"] == 0 for i in range(1, 8)) - assert all(acl_obs[i]["dest_wildcard_id"] == 0 for i in range(1, 8)) - assert all(acl_obs[i]["dest_port_id"] == 0 for i in range(1, 8)) - assert all(acl_obs[i]["protocol_id"] == 0 for i in range(1, 8)) + assert all(acl_obs[i]["position"] == i for i in range(7)) + assert all(acl_obs[i]["permission"] == 0 for i in range(7)) + assert all(acl_obs[i]["source_ip_id"] == 0 for i in range(7)) + assert all(acl_obs[i]["source_wildcard_id"] == 0 for i in range(7)) + assert all(acl_obs[i]["source_port_id"] == 0 for i in range(7)) + assert all(acl_obs[i]["dest_ip_id"] == 0 for i in range(7)) + assert all(acl_obs[i]["dest_wildcard_id"] == 0 for i in range(7)) + assert all(acl_obs[i]["dest_port_id"] == 0 for i in range(7)) + assert all(acl_obs[i]["protocol_id"] == 0 for i in range(7)) def test_firewall_observation(): @@ -75,7 +75,7 @@ def test_firewall_observation(): observation = firewall_observation.observe(firewall.describe_state()) observed_rule = observation["ACL"]["INTERNAL"]["INBOUND"][5] - assert observed_rule["position"] == 4 + assert observed_rule["position"] == 5 assert observed_rule["permission"] == 2 assert observed_rule["source_ip_id"] == 2 assert observed_rule["source_wildcard_id"] == 3 diff --git a/tests/integration_tests/game_layer/observations/test_router_observation.py b/tests/integration_tests/game_layer/observations/test_router_observation.py index 495e102d..ddbbb655 100644 --- a/tests/integration_tests/game_layer/observations/test_router_observation.py +++ b/tests/integration_tests/game_layer/observations/test_router_observation.py @@ -53,7 +53,7 @@ def test_router_observation(): # Observe the state using the RouterObservation instance observed_output = router_observation.observe(router.describe_state()) observed_rule = observed_output["ACL"][5] - assert observed_rule["position"] == 4 + assert observed_rule["position"] == 5 assert observed_rule["permission"] == 2 assert observed_rule["source_ip_id"] == 2 assert observed_rule["source_wildcard_id"] == 3 @@ -77,7 +77,7 @@ def test_router_observation(): ) observed_output = router_observation.observe(router.describe_state()) observed_rule = observed_output["ACL"][2] - assert observed_rule["position"] == 1 + assert observed_rule["position"] == 2 assert observed_rule["permission"] == 1 assert observed_rule["source_ip_id"] == 1 assert observed_rule["source_wildcard_id"] == 1