Rules

Rules are the heart of CFRipper. When running CFRipper the CloudFormation stack will be checked against each rule and the results combined.

Available Rules

CloudFormationAuthenticationRule

Checks that any AWS::CloudFormation::Authentication resource does not contain plain text credentials.

Severity: Medium

Defaults to monitor mode (rule not enforced)

Risk

Secrets are stored in clear text and printed in clear text in the AWS console.

Fix

Do not store credentials in CloudFormation files, use parameters.

Code for fix

Parameters:
  PasswordAuth:
    NoEcho: true
    Description: Some cool password
    MinLength: 8
    Type: String

...

Resources:
  AWS::CloudFormation::Authentication:
    ...
    password:
      Ref: "PasswordAuth"
    ...

CrossAccountCheckingRule

Base class not intended to be instantiated, but inherited from. This class provides common methods used to detect access permissions from other accounts.

Severity: Medium

CrossAccountTrustRule

Checks if the trust policy of a role grants permissions to principals from other accounts. Do not use whole accounts as principals.

Severity: Medium

Risk

It might allow other AWS identities to escalate privileges.

Fix

If cross account permissions are required, the stack should be added to the whitelist for this rule. Otherwise, the access should be removed from the CloudFormation definition.

EBSVolumeHasSSERule

Checks that server side encryption is enabled for all EBS volumes.

Severity: Medium

Defaults to monitor mode (rule not enforced)

Risk

Data that is not encrypted at rest could breach regulatory compliance and allow easier access for an attacker to view any instace storage data of your EC2 instance.

Fix

Enable server-side encryption on EBS volumes.

Code for fix

{
    "Type" : "AWS::EC2::Volume",
    "Properties" : {
        ...
        "Encrypted" : true,
        ...
    }
}

FullWildcardPrincipalRule

Checks for any wildcard principals defined in any statements.

Severity: High

Risk

It might allow other AWS identities to escalate privileges.

Fix

Where possible, restrict the access to only the required resources. For example, instead of Principal: "*", include a list of the roles that need access.

HardcodedRDSPasswordRule

Checks that any RDS clusters or instances aren't exposing their passwords. The rule forbids default password parameters and any missing NoEcho for RDS passwords.

Severity: Medium

Risk

Not setting this correctly can lead to malicious agents attempting to gain access to your RDS instaces with a default password, or by reading the value that will be printed in plain text in the AWS console and logs if NoEcho is not set.

Fix

When defining a password do not use the default value. If you specify a default password and you don’t provide a parameter, it will use the default which can be found clear text in the CloudFormation file.

Code for fix

Parameters:
  MasterUserPassword:
    NoEcho: true
    Description: The database admin account password
    MinLength: 8
    Type: String

...

Resources:
  RDSCluster:
    Type: AWS::RDS::DBCluster
    DeletionPolicy: "Snapshot"
    Properties:
      ...
      MasterUserPassword: !Ref 'MasterUserPassword'
      ...

IAMRoleWildcardActionOnPolicyRule

Checks for use of wildcard characters in all IAM Role policies (including AssumeRolePolicyDocument) and AWS Managed Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html).

Severity: Medium

Defaults to debug mode (rule not enforced)

IAMRolesOverprivilegedRule

Rule that checks for wildcards in resources for a set of actions and restricts managed policies.

Severity: Medium

KMSKeyCrossAccountTrustRule

Checks for KMS keys that allow cross-account principals to get access to the key.

Severity: Medium

Risk

It might allow other AWS identities to read/modify the secrets.

Fix

If cross account permissions are required for KMS access, the stack should be added to the whitelist for this rule. Otherwise, the access should be removed from the CloudFormation definition.

KMSKeyWildcardPrincipal

Check for wildcards in principals in KMS Policies.

Severity: Medium

ManagedPolicyOnUserRule

Checks if any IAM managed policy is applied to a group and not a user.

Severity: Medium

Defaults to monitor mode (rule not enforced)

PartialWildcardPrincipalRule

Checks for any wildcard or account-wide principals defined in any statements. This rule will flag as non-compliant any principals where root or * are included at the end of the value, for example, arn:aws:iam:12345:12345*.

Severity: Medium

Defaults to monitor mode (rule not enforced)

Risk

It might allow other AWS identities or the root access of the account to escalate privileges.

Fix

Where possible, restrict the access to only the required resources. For example, instead of Principal: "*", include a list of the roles that need access.

PolicyOnUserRule

Checks if any IAM policy is applied to a group and not a user.

Severity: Medium

Defaults to monitor mode (rule not enforced)

PrincipalCheckingRule

Abstract class for rules that check principals

Severity: Medium

PrivilegeEscalationRule

Checks for any dangerous IAM actions that could allow privilege escalation and potentially represent a large security risk. See current blacklisted IAM actions.

Severity: Medium

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.

S3BucketPolicyPrincipalRule

Checks for non-whitelisted principals in S3 bucket policies.

Severity: High

Risk

This is designed to block unintended access from third party accounts to your buckets.

Fix

All principals connected to S3 Bucket Policies should be known. CFRipper checks that all principals meet the requirements expected. The list of valid accounts is defined in valid_principals, which is set in the config.

S3BucketPolicyWildcardActionRule

Checks for use of the wildcard * character in the Actions of Policy Documents of S3 Bucket Policies. This rule is a subclass of GenericWildcardPolicyRule.

Severity: Medium

Defaults to debug mode (rule not enforced)

S3BucketPublicReadAclAndListStatementRule

Checks if any S3 bucket policy has a public read ACL and List permission in the bucket policy.

Severity: Medium

Defaults to debug mode (rule not enforced)

Fix

Unless the bucket is hosting static content and needs to be accessed publicly, these bucket policies should be locked down.

S3BucketPublicReadWriteAclRule

Checks if any S3 bucket policy has access control set to PublicReadWrite.

Severity: High

S3CrossAccountTrustRule

Check for cross account access in S3 bucket policies. Cross account access by default should not be allowed.

Severity: Medium

Risk

It might allow other AWS identities to access/modify content of the bucket.

Fix

If cross account permissions are required for S3 access, the stack should be added to the whitelist for this rule. Otherwise, the access should be removed from the CloudFormation definition.

SNSTopicPolicyNotPrincipalRule

Checks if an SNS topic policy has an Allow + a NotPrincipal (exclusive principal).

Severity: Medium

Defaults to monitor mode (rule not enforced)

SNSTopicPolicyWildcardActionRule

Checks for use of the wildcard * character in the Actions of Policy Documents of SQS Queue Policies. This rule is a subclass of GenericWildcardPolicyRule.

Severity: Medium

Defaults to debug mode (rule not enforced)

SQSQueuePolicyNotPrincipalRule

Checks if an SQS Queue policy has an Allow + a NotPrincipal (exclusive principal).

Severity: Medium

Defaults to monitor mode (rule not enforced)

SQSQueuePolicyPublicRule

Checks for wildcard principals in Allow statements in an SQS Queue Policy.

Severity: High

Risk

This is deemed a potential security risk as anyone would be able to interact with your queue.

SQSQueuePolicyWildcardActionRule

Checks for use of the wildcard * character in the Actions of Policy Documents of SQS Queue Policies. This rule is a subclass of GenericWildcardPolicyRule.

Severity: Medium

Defaults to debug mode (rule not enforced)

SecurityGroupIngressOpenToWorld

Checks if a security group has a CIDR open to world on ingress.

Severity: Medium

Fix

Unless required, do not use 0.0.0.0/0 as an ingress rule in your Security group. This is a security risk as your resource will be publicly available.

SecurityGroupMissingEgressRule

Checks that Security Groups are defined with an egress policy, even if this is still allowing all outbound traffic.

Severity: Medium

Defaults to debug mode (rule not enforced)

Risk

If no egress rule is specified, the default is to open all outbound traffic to the world. Whilst some services may need this, it is usually the case that the security group can be locked down more. A NAT instance for example may require a completely open egress policy.

Allowing unrestricted (0.0.0.0/0 or ::/0) outbound/egress access can increase opportunities for malicious activity such as such as Denial of Service (DoS) attacks or Distributed Denial of Service (DDoS) attacks.

Fix

Explicitly defining the egress policy for the security group.

Code for fix

Even in the example below, the egress rule added will allow HTTP traffic out to the world. However, this will pass the rule.

// example from https://stelligent.com/2016/04/07/finding-security-problems-early-in-the-development-process-of-a-cloudformation-template-with-cfn-nag/
{
    "Resources": {
        "sg": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": "some_group_desc",
                "SecurityGroupIngress": {
                    "CidrIp": "10.1.2.3/32",
                    "FromPort": 34,
                    "ToPort": 34,
                    "IpProtocol": "tcp"
                },
                // addition of egress to the `sg` resource
                "SecurityGroupEgress": {
                    "CidrIp": "0.0.0.0/0",
                    "FromPort": 80,
                    "ToPort": 80,
                    "IpProtocol": "tcp"
                },
                "VpcId": "vpc-12345678"
            }
        }
    }
}

SecurityGroupOpenToWorldRule

Checks if security groups have an ingress rule of /0 for ports other than 80 and 443. All other ports should be closed off from public access to prevent a serious security misconfiguration.

Severity: Medium

Fix

Most security groups only need to be access privately, and this can typically be done by specifying the CIDR of a Security Group's ingress to 10.0.0.0/8 or similar (https://en.wikipedia.org/wiki/Private_network).

Code for fix

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Resources": {
        "SecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": "description",
                "SecurityGroupIngress": [
                    // this is compliant. Port 22 (typically SSH) is accessible from the private network only.
                    {
                        "IpProtocol": "tcp",
                        "CidrIp": "10.0.0.0/8",
                        "FromPort": 22,
                        "ToPort": 22
                    },
                    // this is not compliant. Anyone with the IP for this EC2 instance can connect on port 9090.
                    {
                        "IpProtocol": "tcp",
                        "CidrIp": "0.0.0.0/0",
                        "FromPort": 9090,
                        "ToPort": 9090
                    }
                ]
            }
        }
    }
}

Custom Rules

To add custom rules first extend the Rule class. Then implement the invoke method by adding your logic.

    @abstractmethod
    def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result:
        pass

CFRipper uses pycfmodel to create a Python model of the CloudFormation script. This model is passed to the invoke function as the cfmodel parameter. You can use the model's iterate through the resources and other objects of the model and use the helper functions to perform various checks. Look at the current rules for examples.

class S3CrossAccountTrustRule(CrossAccountCheckingRule):
    """
    Check for cross account access in S3 bucket policies. Cross account access by default should not be allowed.

    Risk:
        It might allow other AWS identities to access/modify content of the bucket.

    Fix:
        If cross account permissions are required for S3 access, the stack should be added to the whitelist for this rule.
        Otherwise, the access should be removed from the CloudFormation definition.
    """

    REASON = "{} has forbidden cross-account policy allow with {} for an S3 bucket."

    def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result:
        result = Result()
        for logical_id, resource in cfmodel.Resources.items():
            if isinstance(resource, S3BucketPolicy):
                for statement in resource.Properties.PolicyDocument._statement_as_list():
                    self._do_statement_check(result, logical_id, statement)
        return result

Monitor Mode

By default, each rule has MONITOR_MODE set to false. Monitor model will return the failed rules in another field in the response, instead in the main "failed rules". This way new rules can be tested before they are removed from monitor mode and start triggering alarms.