Path: blob/main/cyberbattle/samples/chainpattern/chainpattern.py
597 views
# Copyright (c) Microsoft Corporation.1# Licensed under the MIT License.23"""Defines a set of networks following a speficic pattern4learnable from the properties associated with the nodes.56The network pattern is:7Start ---> (Linux ---> Windows ---> ... Linux ---> Windows)* ---> Linux[Flag]89The network is parameterized by the length of the central Linux-Windows chain.10The start node leaks the credentials to connect to all other nodes:1112For each `XXX ---> Windows` section, the XXX node has:13- a local vulnerability exposing the RDP password to the Windows machine14- a bunch of other trap vulnerabilities (high cost with no outcome)15For each `XXX ---> Linux` section,16- the Windows node has a local vulnerability exposing the SSH password to the Linux machine17- a bunch of other trap vulnerabilities (high cost with no outcome)1819The chain is terminated by one node with a flag (reward).2021A Node-Property matrix would be three-valued (0,1,?) and look like this:2223===== Initial state24Properties25Nodes L W SQL261 1 0 0272 ? ? ?283 ? ? ?29...301031======= After discovering node 232Properties33Nodes L W SQL341 1 0 0352 0 1 1363 ? ? ?37...381039===========================4041"""4243from cyberbattle.simulation.model import Identifiers, NodeID, NodeInfo44from ...simulation import model as m45from typing import Dict4647DEFAULT_ALLOW_RULES = [48m.FirewallRule("RDP", m.RulePermission.ALLOW),49m.FirewallRule("SSH", m.RulePermission.ALLOW),50m.FirewallRule("HTTPS", m.RulePermission.ALLOW),51m.FirewallRule("HTTP", m.RulePermission.ALLOW),52]5354# Environment constants used for all instances of the chain network55ENV_IDENTIFIERS = Identifiers(56properties=[57"Windows",58"Linux",59"ApacheWebSite",60"IIS_2019",61"IIS_2020_patched",62"MySql",63"Ubuntu",64"nginx/1.10.3",65"SMB_vuln",66"SMB_vuln_patched",67"SQLServer",68"Win10",69"Win10Patched",70"FLAG:Linux",71],72ports=["HTTPS", "GIT", "SSH", "RDP", "PING", "MySQL", "SSH-key", "su"],73local_vulnerabilities=["ScanBashHistory", "ScanExplorerRecentFiles", "SudoAttempt", "CrackKeepPassX", "CrackKeepPass"],74remote_vulnerabilities=["ProbeLinux", "ProbeWindows"],75)767778def prefix(x: int, name: str):79"""Prefix node name with an instance"""80return f"{x}_{name}"818283def rdp_password(index):84"""Generate RDP password for the specified chain link"""85return f"WindowsPassword!{index}"868788def ssh_password(index):89"""Generate SSH password for the specified chain link"""90return f"LinuxPassword!{index}"919293def create_network_chain_link(n: int) -> Dict[NodeID, NodeInfo]:94"""Instantiate one link of the network chain with associated index n"""9596def current(name):97return prefix(n, name)9899def next(name):100return prefix(n + 1, name)101102return {103current("LinuxNode"): m.NodeInfo(104services=[m.ListeningService("HTTPS"), m.ListeningService("SSH", allowedCredentials=[ssh_password(n)])],105firewall=m.FirewallConfiguration(incoming=DEFAULT_ALLOW_RULES, outgoing=DEFAULT_ALLOW_RULES),106value=100,107properties=["MySql", "Ubuntu", "nginx/1.10.3"],108owned_string="Intermediate chain node owned, no intrinsic value",109vulnerabilities=dict(110ProbeLinux=m.VulnerabilityInfo(111description="Probe to check if the node runs Linux",112type=m.VulnerabilityType.REMOTE,113outcome=m.ProbeSucceeded(["Ubuntu"]),114reward_string="Remote machine is running Linux",115cost=5.0,116),117ProbeWindows=m.VulnerabilityInfo(118description="Probe to check if the node runs Windows", type=m.VulnerabilityType.REMOTE, outcome=m.ProbeFailed(), reward_string="Remote machine is not running Windows", cost=5.0119),120ScanBashHistory=m.VulnerabilityInfo(121description="Scan bash history for possible references to other machines",122type=m.VulnerabilityType.LOCAL,123outcome=m.LeakedNodesId([next("WindowsNode")]),124reward_string="Found a reference to a remote Windows node in bash history",125cost=1.0,126),127ScanExplorerRecentFiles=m.VulnerabilityInfo(128description="Scan Windows Explorer recent files for possible references to other machines",129type=m.VulnerabilityType.LOCAL,130outcome=m.ExploitFailed(),131reward_string="Trap: feature not supported on Linux",132cost=10.0,133),134SudoAttempt=m.VulnerabilityInfo(135description="Attempt to sudo into admin user", type=m.VulnerabilityType.LOCAL, outcome=m.ExploitFailed(), reward_string="Trap: suspicious attempt to run sudo", cost=100.0136),137CrackKeepPassX=m.VulnerabilityInfo(138description="Attempt to crack KeepPassX and look for credentials",139type=m.VulnerabilityType.LOCAL,140outcome=m.LeakedCredentials(credentials=[m.CachedCredential(node=next("WindowsNode"), port="RDP", credential=rdp_password(n + 1))]),141reward_string=f"Discovered password to Windows machine {n+1}",142cost=1.0,143),144),145),146next("WindowsNode"): m.NodeInfo(147services=[m.ListeningService("HTTPS"), m.ListeningService("RDP", allowedCredentials=[rdp_password(n + 1)])],148value=100,149properties=["Windows", "Win10", "Win10Patched"],150vulnerabilities=dict(151ProbeLinux=m.VulnerabilityInfo(152description="Probe to check if the node runs Linux", type=m.VulnerabilityType.REMOTE, outcome=m.ProbeFailed(), reward_string="Remote machine is not running Linux", cost=1.0153),154ProbeWindows=m.VulnerabilityInfo(155description="Probe to check if the node runs Windows",156type=m.VulnerabilityType.REMOTE,157outcome=m.ProbeSucceeded(["Windows"]),158reward_string="Remote machine is running Windows",159cost=1.0,160),161ScanBashHistory=m.VulnerabilityInfo(162description="Scan bash history for possible references to other machines",163type=m.VulnerabilityType.LOCAL,164outcome=m.ExploitFailed(),165reward_string="Trap: feature not supported on Windows!",166cost=100.0,167),168ScanExplorerRecentFiles=m.VulnerabilityInfo(169description="Scan Windows Explorer recent files for possible references to other machines",170type=m.VulnerabilityType.LOCAL,171outcome=m.LeakedNodesId([prefix(n + 2, "LinuxNode")]),172reward_string="Found a reference to a remote Linux node in bash history",173cost=1.0,174),175SudoAttempt=m.VulnerabilityInfo(176description="Attempt to sudo into admin user", type=m.VulnerabilityType.LOCAL, outcome=m.ExploitFailed(), reward_string="Trap: feature not supported on Windows!", cost=100.0177),178CrackKeepPassX=m.VulnerabilityInfo(179description="Attempt to crack KeepPassX and look for credentials",180type=m.VulnerabilityType.LOCAL,181outcome=m.ExploitFailed(),182reward_string="Trap: feature not supported on Windows!",183cost=100.0,184),185CrackKeepPass=m.VulnerabilityInfo(186description="Attempt to crack KeepPass and look for credentials",187type=m.VulnerabilityType.LOCAL,188outcome=m.LeakedCredentials(credentials=[m.CachedCredential(node=prefix(n + 2, "LinuxNode"), port="SSH", credential=ssh_password(n + 2))]),189reward_string=f"Discovered password to Linux machine {n+2}",190cost=1.0,191),192),193),194}195196197def create_chain_network(size: int) -> Dict[NodeID, NodeInfo]:198"""Create a chain network with the chain section of specified size.199Size must be an even number200The number of nodes in the network is `size + 2` to account for the start node (0)201and final node (size + 1).202"""203204if size % 2 == 1:205raise ValueError(f"Chain size must be even: {size}")206207final_node_index = size + 1208209nodes = {210"start": m.NodeInfo(211services=[],212value=0,213vulnerabilities=dict(214ScanExplorerRecentFiles=m.VulnerabilityInfo(215description="Scan Windows Explorer recent files for possible references to other machines",216type=m.VulnerabilityType.LOCAL,217outcome=m.LeakedCredentials(credentials=[m.CachedCredential(node=prefix(1, "LinuxNode"), port="SSH", credential=ssh_password(1))]),218reward_string="Found a reference to a remote Linux node in bash history",219cost=1.0,220)221),222agent_installed=True,223reimagable=False,224),225prefix(final_node_index, "LinuxNode"): m.NodeInfo(226services=[m.ListeningService("HTTPS"), m.ListeningService("SSH", allowedCredentials=[ssh_password(final_node_index)])],227value=1000,228owned_string="FLAG: flag discovered!",229properties=["MySql", "Ubuntu", "nginx/1.10.3", "FLAG:Linux"],230vulnerabilities=dict(),231),232}233234# Add chain links235for i in range(1, size, 2):236nodes.update(create_network_chain_link(i))237238return nodes239240241def new_environment(size) -> m.Environment:242return m.Environment(network=m.create_network(create_chain_network(size)), vulnerability_library=dict([]), identifiers=ENV_IDENTIFIERS)243244245