def test_pathEvaluation(render=True): # TODO: Make this test more useful root = Root("Root") goal = Goal("Goal") brain = Brain() # One path to vicotry (Start->a->b>c->goal) a = root.add(Action("A", pSuccess=100, cost=500)) b = a.add(Action("B", pSuccess=50, cost=500)) c = b.add(Action("C", pSuccess=50, cost=500)) _ = c.add(goal) # Update model with a second path to victory (Start->a->bb->goal) bb = a.add(Action("BB", pSuccess=100, cost=20000)) bb.connectTo(goal) paths = [] _ = Brain().pathsToVictory(root, paths) print(paths) assert len(paths) == 2 res = brain.evaluatePath(paths[1]) # a -> bb -> goal assert res["attackCost"] == 20500 assert res["pSuccess"] == 100 # a * bb if render: Renderer().render(root=root, fname=inspect.currentframe().f_code.co_name)
def test_insertBlockBetween_100perecent(render=False): # Prep root = Root("Start") goal = Goal("Finish") a = root.action("A") b = a.action("B") c = b.action("C") c.connectTo(goal) #Before assert (len(b.edges) == 1) # Create a second block with a pSuccess of 100 block = Block(label="Test Block", implemented=True, cost=5000, pDefend=100) # The tested function block.insertBetween(b, c) # After assert ( len(b.edges) == 1 ) # As the block has 100% chance of working, c should be unreachable assert (b.edges[0].childNode == block) if render: Renderer().render(root=root, fname=inspect.currentframe().f_code.co_name)
def test_instertBlockBetweenPatiallySuccessfulActions(render=True): # Prep root = Root("Start") goal = Goal("Finish") a = root.add(Action(label="A", pSuccess=80)) b = a.action("B") c = b.action("C") c.connectTo(goal) # a has an 80percent chance of working. # lets add a blocker that's 20% effective block = Block(label="Test Block", implemented=True, cost=5000, pDefend=50) # The tested function block.insertBetween(a, b) assert (len(a.edges) == 1) assert (a.edges[0].childNode == block) assert (len(block.edges) == 1) assert (block.edges[0].childNode == b) # The tested function: if render: Renderer().render(root=root, fname=inspect.currentframe().f_code.co_name)
def test_insertBetween_50perecent(render=False): # Prep root = Root("Start") goal = Goal("Finish") a = root.action("A") b = a.action("B") c = b.action("C") c.connectTo(goal) # Verify prep assert (len(a.edges) == 1) assert (a.edges[0].childNode == b) # Create a block block = Block(label="Test Block", implemented=True, cost=5000, pDefend=50) # The tested function block.insertBetween(a, b) # After assert (len(a.edges) == 1) assert (len(block.edges) == 1) assert (a.edges[0].childNode.edges[0].childNode == b) if render: Renderer().render(root=root, fname=inspect.currentframe().f_code.co_name)
def test_single_pathToVictory(render=False): root = Root("Root") goal = Goal("Goal") # Test with just one action root.add(Action(label="A")).add(goal) brain = Brain() paths = brain.pathsToVictory(root) print(paths) assert len(paths) == 1 assert len(paths[0]) == 3 if render: Renderer().render(root=root, fname=inspect.currentframe().f_code.co_name)
def test_buildTree(render=True): root = basicTree() brain = Brain() # Check root is correct type assert isinstance(root, Root) paths = brain.pathsToVictory(root) for path in paths: brain.evaluatePath(path) assert len(paths) == 3 if render: Renderer().render(root=root, fname=inspect.currentframe().f_code.co_name)
def test_double_pathToVictory(render=True): root = Root("Root XX") goal = Goal("Goal") # Test with just one action root.add(Action(label="A")).add(goal) root.add(Action(label="B")).add(goal) brain = Brain() paths = brain.pathsToVictory(root) print(paths) assert(len(paths) == 2) assert(len(paths[0]) == 3) assert(len(paths[1]) == 3) if render: Renderer().render( root=root, fname=inspect.currentframe().f_code.co_name )
def test_quad_pathToVictory(render=True): root = Root("Root XXXX") goal = Goal("Goal") # Test with just one action root.add(Action(label="A")).add(goal) root.add(Action(label="B")).add(goal) root.add(Action(label="C")).add(goal) root.add(Action(label="D")).add(goal) brain = Brain() paths = brain.pathsToVictory(root) assert len(paths) == 4 assert len(paths[0]) == 3 assert len(paths[1]) == 3 assert len(paths[2]) == 3 assert len(paths[3]) == 3 if render: Renderer().render(root=root, fname=inspect.currentframe().f_code.co_name)
def test_pathEvaluationWithBlock(render=True): root = Root("Root") goal = Goal("Goal") brain = Brain() a = root.add(Action("A",pSuccess=100, cost=500)) b = a.add(Action("b",pSuccess=70, cost=500)) b.add(goal) paths = [] _ = brain.pathsToVictory(root, paths) assert(len(paths)==1) res = brain.evaluatePath(paths[0]) # Check results are correct before we add a block assert(res['attackCost']==1000) assert(res['pSuccess']==70) assert(a.pSuccess==100) block = Block(label="FIREWALL",implemented=True,cost=0,pDefend=50) block.insertBetween(a,b) paths = [] _ = brain.pathsToVictory(root, paths) res = brain.evaluatePath(paths[0]) print(res) assert(res['attackCost']==1000) assert(res['pSuccess']==35) if render: Renderer().render( root=root, fname=inspect.currentframe().f_code.co_name )
from attacktree.models import Action, Block, Detect, Discovery, Edge from attacktree.renderer import Renderer with Renderer(root="Reality", goal="Attacker gets data from bucket") as graph: apiCache = Action( label="Search API Caches", chain="recon", cost=0, time=3, objective="Discover bucket paths", pSuccess=1.0, ) graph.root.add(apiCache, edge_label="#Yolosec") s3urls = Discovery( label="S3 Urls", description="The URL paths to various S3 buckets", sensitivity=3, value=0, ) apiCache.add(s3urls, edge_label="#Yolosec") downloadFiles = Action( chain="exfiltration", label="Download files from all buckets", cost=0, time=1, objective="Access confidential information stored in S3", pSuccess=1.0, detections=["CloudWatch", "DLP"],
def test_contextManager(render=False): with Renderer(root="Reality", goal="Attacker gets data from bucket") as graph: pwd = graph.root.action("Use password") block = pwd.block("Block", implemented=False) block.connectTo(graph.goal)
describePods = creds.action("Call KubeAPI describe pods") pods = describePods.discover( "100+ customer pods on 120 nodes\nEach customer has their own namespace") versions = describePods.discover( "OLD Kubernetes versions v1.8.4, v1.9.10, v1.10.9") anonymousAccess = describePods.discover("Kubelets run with anonymous access") otherKubelet = anonymousAccess.action("Access another customer's kubelet") blockedbyFirewall = otherKubelet.block("Blocked by firewall", implemented=True) cve = versions.discover("CVE-2018-1002102 kube-api follows 302 redirect") exploit2 = cve.action("Attempt to redirect to kube-api pod") fail = exploit2.block( "ACI uses a 'bridge' POD which is not impacted by this issue", implemented=True) interesting = exploit2.discover( "ServiceAccount in 'AuthorizationHeader' of Exec requests") bridgeToken = interesting.discover( "Decoded JWT shows this token belongs to 'bridge' service") accessReview = bridgeToken.action( "Call SelfSubjectAccessReview with 'bridge' token") privs = accessReview.discover("Cluster-wide permissions\npods/exec privilege") gameOver = privs.action("Exec into shell on kube-API") gameOver.add(goal) Renderer().render(root, fname="aciBreakout")
from attacktree.models import Action, Block, Detect, Discovery, Edge from attacktree.renderer import Renderer with Renderer(root="Internet", goal="Launch Containers") as graph: breakApplication = Action(label="RCE in application") graph.root.add(breakApplication) patch = Block(label="Keep containers up to date", implemented=True) breakApplication.add(patch) executeSiloScape = Action(label="Execute Siloscape") breakApplication.add(executeSiloScape) systemPrivileges = Discovery(label="Privileged Access") executeSiloScape.add(systemPrivileges) symLinkDrive = Action(label="SymLink root volume") systemPrivileges.add(symLinkDrive) kubeConfig = Action(label="Find Kubernetes creds on disk") symLinkDrive.add(kubeConfig) deployMalicious = Action(label="Deploy malicious containers") kubeConfig.add(deployMalicious) runWindowsContainersWithLowPrivilege = Block( label="Windows containers have low privilege", implemented=False) deployMalicious.add(runWindowsContainersWithLowPrivilege) deployMalicious.add(graph.goal)