-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor, add more Rules and use new pycfmodel (#134)
* Refactor, add more Rules and use new pycfmodel * Fix typo * Detect privilege escalation in any policy from any resource (not only IAMPolicies) * Improve tests for privilege escalation * Improve tests and docs * Bump version to 0.21.0 * Fix flake8 issue * Improve ABC implementation of dangerous actions * Update cfripper/rules/base_rules.py Co-authored-by: Oliver Crawford <16978487+ocrawford555@users.noreply.github.com> * Update cfripper/rules/sns_topic_policy.py Co-authored-by: Oliver Crawford <16978487+ocrawford555@users.noreply.github.com> * Update cfripper/rules/sns_topic_policy.py Co-authored-by: Oliver Crawford <16978487+ocrawford555@users.noreply.github.com> * Remove leftover print * Fix test after copy paste error * Add failure when and only when there are dangerous actions Co-authored-by: Oliver Crawford <16978487+ocrawford555@users.noreply.github.com>
- Loading branch information
1 parent
825d5ef
commit a9bd092
Showing
24 changed files
with
472 additions
and
175 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
VERSION = (0, 20, 1) | ||
VERSION = (0, 21, 0) | ||
|
||
__version__ = ".".join(map(str, VERSION)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,38 @@ | ||
__all__ = ["PrivilegeEscalationRule"] | ||
|
||
from typing import Dict, Optional | ||
|
||
from pycfmodel.model.cf_model import CFModel | ||
from pycfmodel.model.resources.iam_policy import IAMPolicy | ||
from pycfmodel.model.resources.resource import Resource | ||
|
||
from cfripper.model.enums import RuleGranularity | ||
from cfripper.model.result import Result | ||
from cfripper.rules.base_rules import Rule | ||
from cfripper.rules.base_rules import BaseDangerousPolicyActions | ||
|
||
|
||
class PrivilegeEscalationRule(Rule): | ||
class PrivilegeEscalationRule(BaseDangerousPolicyActions): | ||
""" | ||
Checks for any dangerous IAM actions that could allow privilege escalation and potentially | ||
represent a large security risk. | ||
See [current blacklisted IAM actions](https://github.com/Skyscanner/cfripper/blob/master/cfripper/rules/privilege_escalation.py#L29). | ||
Fix: | ||
Unless strictly necessary, do not use actions in the IAM action blacklist. CloudFormation files that do require these | ||
actions should be added to the whitelist. | ||
Unless strictly necessary, do not use actions in the IAM action blacklist. CloudFormation files that do require | ||
these actions should be added to the whitelist. | ||
""" | ||
|
||
GRANULARITY = RuleGranularity.RESOURCE | ||
REASON = "{} has blacklisted IAM action {}" | ||
IAM_BLACKLIST = set( | ||
action.lower() | ||
for action in [ | ||
"iam:CreateAccessKey", | ||
"iam:CreateLoginProfile", | ||
"iam:UpdateLoginProfile", | ||
"iam:AttachUserPolicy", | ||
"iam:AttachGroupPolicy", | ||
"iam:AttachRolePolicy", | ||
"iam:PutUserPolicy", | ||
"iam:PutGroupPolicy", | ||
"iam:PutRolePolicy", | ||
"iam:CreatePolicy", | ||
"iam:AddUserToGroup", | ||
"iam:UpdateAssumeRolePolicy", | ||
"iam:CreatePolicyVersion", | ||
"iam:SetDefaultPolicyVersion", | ||
] | ||
) | ||
|
||
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: | ||
result = Result() | ||
for logical_id, resource in cfmodel.Resources.items(): | ||
if isinstance(resource, IAMPolicy): | ||
policy_actions = set(action.lower() for action in resource.Properties.PolicyDocument.get_iam_actions()) | ||
for violation in policy_actions.intersection(self.IAM_BLACKLIST): | ||
self.add_failure_to_result( | ||
result, self.REASON.format(logical_id, violation), resource_ids={logical_id} | ||
) | ||
return result | ||
GRANULARITY = RuleGranularity.ACTION | ||
REASON = "{} has blacklisted IAM actions: {}" | ||
RESOURCE_TYPES = (Resource,) | ||
DANGEROUS_ACTIONS = [ | ||
"iam:AddUserToGroup", | ||
"iam:AttachGroupPolicy", | ||
"iam:AttachRolePolicy", | ||
"iam:AttachUserPolicy", | ||
"iam:CreateAccessKey", | ||
"iam:CreateLoginProfile", | ||
"iam:CreatePolicy", | ||
"iam:CreatePolicyVersion", | ||
"iam:PutGroupPolicy", | ||
"iam:PutRolePolicy", | ||
"iam:PutUserPolicy", | ||
"iam:SetDefaultPolicyVersion", | ||
"iam:UpdateAssumeRolePolicy", | ||
"iam:UpdateLoginProfile", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
__all__ = ["SNSTopicPolicyNotPrincipalRule", "SNSTopicDangerousPolicyActionsRule"] | ||
|
||
|
||
from pycfmodel.model.resources.sns_topic_policy import SNSTopicPolicy | ||
|
||
from cfripper.model.enums import RuleGranularity, RuleRisk | ||
from cfripper.model.result import Result | ||
from cfripper.rules.base_rules import BaseDangerousPolicyActions, ResourceSpecificRule | ||
|
||
|
||
class SNSTopicPolicyNotPrincipalRule(ResourceSpecificRule): | ||
""" | ||
Checks if an SNS topic policy has an Allow + a NotPrincipal. | ||
Risk: | ||
AWS **strongly** recommends against using `NotPrincipal` in the same policy statement as `"Effect": "Allow"`. | ||
Doing so grants the permissions specified in the policy statement to all principals except the one named | ||
in the `NotPrincipal` element. By doing this, you might grant access to anonymous (unauthenticated) users. | ||
""" | ||
|
||
GRANULARITY = RuleGranularity.RESOURCE | ||
REASON = "SNS Topic policy {} should not allow Allow and NotPrincipal at the same time" | ||
RESOURCE_TYPES = (SNSTopicPolicy,) | ||
|
||
def resource_invoke(self, resource: SNSTopicPolicy, logical_id: str) -> Result: | ||
result = Result() | ||
for statement in resource.Properties.PolicyDocument._statement_as_list(): | ||
if statement.NotPrincipal: | ||
self.add_failure_to_result(result, self.REASON.format(logical_id), resource_ids={logical_id}) | ||
return result | ||
|
||
|
||
class SNSTopicDangerousPolicyActionsRule(BaseDangerousPolicyActions): | ||
""" | ||
Checks for dangerous permissions in Action statements in an SNS Topic Policy. | ||
Risk: | ||
This is deemed a potential security risk as it could allow privilege escalation. | ||
""" | ||
|
||
REASON = "SNS Topic policy {} should not not include the following dangerous actions: {}" | ||
RISK_VALUE = RuleRisk.MEDIUM | ||
RESOURCE_TYPES = (SNSTopicPolicy,) | ||
|
||
DANGEROUS_ACTIONS = [ | ||
"sns:AddPermission", | ||
"sns:RemovePermission", | ||
"sns:TagResource", | ||
"sns:UntagResource", | ||
] |
Oops, something went wrong.