def resolve(self): IDP = [ "AWS::Iam::User", "AWS::Iam::Role", "AWS::Iam::Group", "AWS::Iam::Policy" ] RBP = {"AWS::S3::Bucket": "Policy", "AWS::Iam::Role": "Trusts"} (principals, actions, trusts) = (Elements(), Elements(), Elements()) resources = self.get("Resource") + self.get("Generic") print("[*] Resolving actions and resources\n") # Resolve actions for resource in self.get("Resource"): self._print(f"[*] Processing {resource}") # Identity Based Policies (Inline and Managed) if resource.labels()[0] in IDP: count = len(actions) actions.extend( IdentityBasedPolicy(resource, resources).resolve()) diff = len(actions) - count if diff > 0: self._print(f"[+] Identity based Policy ({resource}) " "resolved to {diff} action(s)") if resource.labels()[0] in RBP.keys(): # Bucket ACLs if resource.type("AWS::S3::Bucket"): count = len(actions) acl = BucketACL(resource, resources) principals.extend( [p for p in acl.principals() if p not in principals]) actions.extend( [a for a in acl.resolve() if a not in actions]) diff = len(actions) - count if diff > 0: print(f"[+] Bucket ACL ({resource}) " "resolved to {diff} action(s)") # Resource Based Policies rbp = ResourceBasedPolicy(resource, resources, keys=[RBP[resource.labels()[0]]]) if len(rbp.principals()) > 0: count = len(actions) resolved = rbp.resolve() principals.extend([ p for p in rbp.principals() if p not in principals and str(p) != RESOURCES. types["AWS::Account"].format(Account=self.account_id) ]) # TODO: This code should be moved to 'ResourceBasedPolicy' and override resolve(). # For Roles, actions imply a TRUSTS relationship. Only those beginning # with sts:Assume are considered valid. for action in [ a for a in resolved if "AWS::Iam::Role" not in resource.labels() or str(a).startswith("sts:AssumeRole") ]: if action.source().type("AWS::Account") \ and action.source().properties()["Arn"].split(':')[4] == self.account_id: if "AWS::Iam::Role" in resource.labels(): trusts.extend([ Trusts(properties=action.properties(), source=action.target(), target=e) for e in self.entities ]) # This case appears redundant for Buckets else: if not action.source().type("AWS::Domain"): actions.append(action) if "AWS::Iam::Role" in resource.labels(): trusts.append( Trusts(properties=action.properties(), source=action.target(), target=action.source())) diff = len(actions) - count if diff > 0: print(f"[+] Resource based policy ({resource}) " "resolved to {diff} action(s)") principals = [p for p in principals if p not in self] self.extend(principals) self.extend(actions) self.extend(trusts) sys.stdout.write("\033[F\033[K") print( f"[+] Produced {len(principals)} new principals and {len(actions)} actions\n" )
def load_actions(self): self.add( Node(labels=["CatchAll"], properties={ "Name": "CatchAll", "Description": "A sinkhole for actions affecting unknown resource types." })) # Resource types Actions affect resources = Elements(e for e in self if any( [l in ["Resource", "Generic", "CatchAll"] for l in e.labels()])) # IAM entities entities = Elements( e for e in self.get("Resource") if e.label() in ['AWS::Iam::User', 'AWS::Iam::Role']) for resource in self.console.tasklist( "Resolving Policy information", iterables=self.get("Resource"), done="Added Action relationships"): # Identity-based policies (inline and managed) if resource.label() in [ "AWS::Iam::User", "AWS::Iam::Group", "AWS::Iam::Role", "AWS::Iam::Policy" ]: self.update(IdentityBasedPolicy(resource, resources).actions()) # Resource-based policies if resource.label() in [ "AWS::S3::Bucket", "AWS::S3::Object", ]: resource_based_policy = ResourceBasedPolicy( resource=resource, resources=resources, keys="Policy") self.update(resource_based_policy.principals()) self.update(resource_based_policy.actions()) # Assume role policy documents if resource.label() in ["AWS::Iam::Role"]: resource_based_policy = ResourceBasedPolicy( resource=resource, resources=resources, keys="Trusts") # Skip AWS::Domain principals self.update( Elements( principal for principal in resource_based_policy.principals() if not principal.type("AWS::Domain"))) # Only actions beginning with sts:AssumeRole are valid for action in [ action for action in resource_based_policy.actions() if str(action).startswith("sts:AssumeRole") ]: # This role trusts all IAM entities within this account if (action.source().type("AWS::Account") and len(action.source().id().split(':')) >= 5 and action.source().id().split(':')[4] == self.account): self.update( Elements( Trusts(properties=action.properties(), source=action.target(), target=entity) for entity in entities)) else: # Skip AWS::Domain actions if action.source().type("AWS::Domain"): continue self.add(action) self.add( Trusts(properties=action.properties(), source=action.target(), target=action.source())) # ACLs (bucket & objects) if resource.label() in ["AWS::S3::Bucket", "AWS::S3::Object"]: acl = BucketACL(resource, resources) \ if resource.label() == "AWS::S3::Bucket" \ else ObjectACL(resource, resources) self.update(acl.principals()) self.update(acl.actions())