class TestSensitiveAccess(unittest.TestCase):
    """Test class for single value condition too permissive auditor"""
    example_policy_string = """
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "s3:GetObject"
              ],
              "Resource": "arn:aws:s3:::secretbucket/*",
              "Condition": {
                  "ForAllValues:StringEquals": {
                      "aws:ResourceTag/Tag": [
                          "Value"
                      ]

                  }
              }
            }
          ]
        }
    """
    policy = analyze_policy_string(example_policy_string,
                                   include_community_auditors=True)
    assert_equal(policy.finding_ids,
                 set(["SINGLE_VALUE_CONDITION_TOO_PERMISSIVE"]))
예제 #2
0
    def test_resource_effectively_star(self):
        policy = analyze_policy_string("""{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CloudtrailReadTrail",
      "Effect": "Allow",
      "Action": [
        "cloudtrail:GetEventSelectors",
        "cloudtrail:PutEventSelectors"
      ],
      "Resource": [
        "arn:*:cloudtrail:*:*:trail/*"
      ]
    }
  ]
}""")

        assert_equal(
            policy.finding_ids,
            set(["RESOURCE_EFFECTIVELY_STAR"]),
            "Resource policy spans all Cloudtrails even without an asterisk.",
        )

        policy = analyze_policy_string("""{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "CloudtrailReadTrail",
          "Effect": "Allow",
          "Action": [
            "cloudtrail:GetEventSelectors",
            "cloudtrail:PutEventSelectors"
          ],
          "Resource": [
            "arn:aws:cloudtrail:us-east-1:000000000000:trail/*"
          ]
        }
      ]
    }""")

        assert_equal(
            policy.finding_ids,
            set(),
            "Resource policy is scoped by AWS partition, account and region and is therefore not 'STAR'.",
        )
예제 #3
0
 def test_resource_bad(self):
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": "s3:listallmybuckets",
     "Resource": "s3"}}""")
     assert_equal(len(policy.findings), 1)
예제 #4
0
 def test_analyze_policy_string_no_action(self):
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Resource": "*"}}""")
     assert_false(
         len(policy.findings) == 0, "Policy does not have an Action")
예제 #5
0
    def test_notprincipal_allow(self):
        # NotPrincipal is OK with Effect: Deny. This explcitly omits these
        # users from the list of Principals denied access to this resource
        # This example is taken from https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notprincipal.html#specifying-notprincipal
        policystr = """{
          "Version": "2012-10-17",
          "Statement": [{
            "Effect": "Deny",
            "NotPrincipal": {"AWS": [
              "arn:aws:iam::444455556666:user/Bob",
              "arn:aws:iam::444455556666:root"
            ]},
            "Action": "s3:*",
            "Resource": [
              "arn:aws:s3:::BUCKETNAME",
              "arn:aws:s3:::BUCKETNAME/*"
            ]
          }]
        }"""

        policy = analyze_policy_string(policystr,
                                       include_community_auditors=True)

        assert_equal(policy.finding_ids, set())

        # This implicitly allows everyone _except_ Bob to access BUCKETNAME!
        policystr = """{
          "Version": "2012-10-17",
          "Statement": [{
            "Effect": "Allow",
            "NotPrincipal": {"AWS": [
              "arn:aws:iam::444455556666:user/Bob",
            ]},
            "Action": "s3:*",
            "Resource": [
              "arn:aws:s3:::BUCKETNAME",
              "arn:aws:s3:::BUCKETNAME/*"
            ]
          }]
        }"""

        policy = analyze_policy_string(policystr,
                                       include_community_auditors=True)

        assert_equal(policy.finding_ids,
                     S3_STAR_FINDINGS | {"NOTPRINCIPAL_WITH_ALLOW"})
예제 #6
0
    def test_resource_policy_privilege_escalation(self):
        # This policy is actually granting essentially s3:* due to the ability to put a policy on a bucket
        policy = analyze_policy_string("""{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": ["s3:GetObject", "s3:PutBucketPolicy"],
        "Resource": "*"
        }}""")
        assert_false(
            len(policy.findings) == 0, "Resource policy privilege escalation")

        policy = analyze_policy_string("""{
    "Version": "2012-10-17",
    "Statement": [
        {
    "Action": [
        "s3:ListBucket",
        "s3:Put*",
        "s3:Get*",
        "s3:*MultipartUpload*"
    ],
    "Resource": [
        "*"
    ],
    "Effect": "Allow"
},
{
    "Action": [
        "s3:*Policy*",
        "sns:*Permission*",
        "sns:*Delete*",
        "s3:*Delete*",
        "sns:*Remove*"
    ],
    "Resource": [
        "*"
    ],
    "Effect": "Deny"
}
        ]}""")

        assert_false(
            len(policy.findings) == 0,
            "Resource policy privilege escalation across two statement",
        )
예제 #7
0
    def test_condition_multiple(self):
        # Both good
        policy = analyze_policy_string(
            """{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "s3:listbucket",
        "Resource": "arn:aws:s3:::bucket-name",
        "Condition": {
            "DateGreaterThan" :{"aws:CurrentTime" : "2019-07-16T12:00:00Z"},
            "StringEquals": {"s3:prefix":["home/${aws:username}/*"]}
        } }}"""
        )
        assert_equal(len(policy.findings), 0)

        # First bad
        policy = analyze_policy_string(
            """{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "s3:listbucket",
        "Resource": "arn:aws:s3:::bucket-name",
        "Condition": {
            "DateGreaterThan" :{"aws:CurrentTime" : "bad"},
            "StringEquals": {"s3:prefix":["home/${aws:username}/*"]}
        } }}"""
        )
        assert_false(len(policy.findings) == 0, "First condition is bad")

        # Second bad
        policy = analyze_policy_string(
            """{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "s3:listbucket",
        "Resource": "arn:aws:s3:::bucket-name",
        "Condition": {
            "DateGreaterThan" :{"aws:CurrentTime" : "2019-07-16T12:00:00Z"},
            "StringEquals": {"s3:x":["home/${aws:username}/*"]}
        } }}"""
        )
        assert_false(len(policy.findings) == 0, "Second condition is bad")
예제 #8
0
 def test_resource_good(self):
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": "s3:getobject",
     "Resource": "arn:aws:s3:::my_corporate_bucket/*"}}""")
     print(policy.findings)
     assert_equal(len(policy.findings), 0)
예제 #9
0
 def test_condition_mismatch(self):
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": ["ec2:*", "s3:*"],
     "Resource": "*",
     "Condition": {"StringNotEquals": {"iam:ResourceTag/status":"prod"}} }}"""
                                    )
     assert_false(len(policy.findings) == 0, "Condition mismatch")
예제 #10
0
 def test_condition(self):
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": "s3:listbucket",
     "Resource": "arn:aws:s3:::bucket-name",
     "Condition": {"DateGreaterThan" :{"aws:CurrentTime" : "2019-07-16T12:00:00Z"}} }}"""
                                    )
     assert_equal(len(policy.findings), 0)
예제 #11
0
    def test_condition_operator(self):
        policy = analyze_policy_string(
            """{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "s3:listbucket",
        "Resource": "arn:aws:s3:::bucket-name",
        "Condition": {"StringEqualsIfExists": {"s3:prefix":["home/${aws:username}/*"]}} }}""",
            ignore_private_auditors=True,
        )
        assert_equal(policy.finding_ids, set())

        policy = analyze_policy_string(
            """{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "s3:listbucket",
        "Resource": "arn:aws:s3:::bucket-name",
        "Condition": {"bad": {"s3:prefix":["home/${aws:username}/*"]}} }}""",
            ignore_private_auditors=True,
        )
        assert_equal(
            policy.finding_ids,
            set(["UNKNOWN_OPERATOR", "MISMATCHED_TYPE"]),
            "Unknown operator",
        )

        policy = analyze_policy_string(
            """{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "s3:listbucket",
        "Resource": "arn:aws:s3:::bucket-name",
        "Condition": {"NumericEquals": {"s3:prefix":["home/${aws:username}/*"]}} }}""",
            ignore_private_auditors=True,
        )
        assert_equal(
            policy.finding_ids, set(["MISMATCHED_TYPE"]), "Operator type mismatch"
        )
예제 #12
0
 def test_analyze_policy_string_opposites(self):
     # Policy contains Action and NotAction
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": "s3:listallmybuckets",
     "NotAction": "s3:listallmybuckets",
     "Resource": "*"}}""")
     assert_false(
         len(policy.findings) == 0, "Policy contains Action and NotAction")
예제 #13
0
 def test_analyze_policy_string_invalid_sid(self):
     policy = analyze_policy_string(
         """{
 "Version": "2012-10-17",
 "Statement": {
     "Sid": "Statement With Spaces And Special Chars!?",
     "Effect": "Allow",
     "Action": "s3:listallmybuckets",
     "Resource": "*"}}"""
     )
     assert_false(len(policy.findings) == 0, "Policy statement has invalid Sid")
예제 #14
0
 def test_condition_bad_key(self):
     policy = analyze_policy_string(
         """{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": "s3:listbucket",
     "Resource": "arn:aws:s3:::bucket-name",
     "Condition": {"DateGreaterThan" :{"bad" : "2019-07-16T12:00:00Z"}} }}"""
     )
     assert_false(len(policy.findings) == 0, "Policy has bad key in Condition")
예제 #15
0
 def test_bad_mfa_condition(self):
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": "*",
     "Resource": "*",
     "Condition": {"Bool": {"aws:MultiFactorAuthPresent":"false"}}
     }}""")
     assert_false(
         len(policy.findings) == 0, "Policy contains bad MFA check")
예제 #16
0
    def check_policy(self, name, document):
        policy = parliament.analyze_policy_string(json.dumps(document))
        for permission in REQUIRED_PERMISSIONS:
            if policy.get_allowed_resources(*permission.split(":")) != ["*"]:
                self._failure(
                    f"Discarding Instance Policy {name}\n\tMissing permission {permission}"
                )
                return False

        self._success(f"Found Valid SSM Instance Policy {name}")
        return True
예제 #17
0
 def test_analyze_policy_string_no_action(self):
     policy = analyze_policy_string(
         """{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Resource": "*"}}""",
         ignore_private_auditors=True,
     )
     assert_equal(policy.finding_ids, set(["MALFORMED"]),
                  "Policy does not have an Action")
예제 #18
0
    def test_notresource_allow(self):
        # NotResource is OK with Effect: Deny. This denies access to
        # all S3 buckets except Payroll buckets. This example is taken from
        # https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notresource.html
        policystr = """{
          "Version": "2012-10-17",
          "Statement": {
            "Effect": "Deny",
            "Action": "s3:*",
            "NotResource": [
              "arn:aws:s3:::HRBucket/Payroll",
              "arn:aws:s3:::HRBucket/Payroll/*"
            ]
          }
        }"""

        policy = analyze_policy_string(policystr,
                                       include_community_auditors=True)
        assert_equal(policy.finding_ids, set())

        # According to AWS documentation, "This statement is very dangerous,
        # because it allows all actions in AWS on all resources except the
        # HRBucket S3 bucket." See:
        # https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notresource.html#notresource-element-combinations
        policystr = """{
          "Version": "2012-10-17",
          "Statement": {
            "Effect": "Allow",
            "Action": "s3:*",
            "NotResource": [
              "arn:aws:s3:::HRBucket/Payroll",
              "arn:aws:s3:::HRBucket/Payroll/*"
            ]
          }
        }"""

        policy = analyze_policy_string(policystr,
                                       include_community_auditors=True)

        assert_equal(policy.finding_ids,
                     S3_STAR_FINDINGS | {"NOTRESOURCE_WITH_ALLOW"})
예제 #19
0
 def test_condition_action_specific_bad_type(self):
     # s3:signatureage requires a number
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": "s3:listbucket",
     "Resource": "arn:aws:s3:::bucket-name",
     "Condition": {"StringEquals": {"s3:signatureage":"bad"}} }}""")
     print(policy.findings)
     assert_false(
         len(policy.findings) == 0, 'Wrong type, "bad" should be a number')
예제 #20
0
 def test_analyze_policy_string_correct_multiple_statements_and_actions(
         self):
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": [{
     "Effect": "Allow",
     "Action": "s3:listallmybuckets",
     "Resource": "*"},
     {
     "Effect": "Allow",
     "Action": "iam:listusers",
     "Resource": "*"}]}""")
     assert_equal(len(policy.findings), 0)
예제 #21
0
def validate_file(filename):
    with (open(filename, 'r')) as file:
        tf = hcl2.load(file)

    findings = []

    # Validate data.aws_iam_policy_document
    for policy_document in filter(
            lambda x: x.get('aws_iam_policy_document', None),
            tf.get('data', [])):
        iam_statements = []
        policy_name = list(
            policy_document['aws_iam_policy_document'].keys())[0]
        for statement_data in policy_document['aws_iam_policy_document'][
                policy_name]['statement']:
            # Don't check assume role policies; these will have spurious findings for
            # "Statement contains neither Resource nor NotResource"
            actions = statement_data.get('actions')[0]
            if actions == ['sts:AssumeRole'
                           ] or actions == ['sts:AssumeRoleWithSAML']:
                continue

            iam_statements.append(mock_iam_statement_from_tf(statement_data))

        policy_string = policy_template.format(
            iam_statements=json.dumps(iam_statements))
        findings += parliament.analyze_policy_string(policy_string).findings

    # Validate resource.aws_iam_policy
    for policy in filter(lambda x: x.get('aws_iam_policy', None),
                         tf.get('resource', [])):
        try:
            policy_string = policy['aws_iam_policy']['policy']['policy'][0]
        except KeyError:
            continue

        findings += parliament.analyze_policy_string(policy_string).findings

    return findings
예제 #22
0
 def test_analyze_policy_string_correct_simple(self):
     policy = analyze_policy_string(
         """{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": "s3:listallmybuckets",
     "Resource": "*"}}""",
         ignore_private_auditors=True,
     )
     assert_equal(
         policy.finding_ids, set(),
     )
예제 #23
0
 def test_analyze_policy_string_invalid_sid(self):
     policy = analyze_policy_string(
         """{
 "Version": "2012-10-17",
 "Statement": {
     "Sid": "Statement With Spaces And Special Chars!?",
     "Effect": "Allow",
     "Action": "s3:listallmybuckets",
     "Resource": "*"}}""",
         ignore_private_auditors=True,
     )
     assert_equal(policy.finding_ids, set(["INVALID_SID"]),
                  "Policy statement has invalid Sid")
예제 #24
0
    def test_resource_policy_privilege_escalation_at_bucket_level(self):
        policy = analyze_policy_string(
            """{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": ["s3:GetObject", "s3:PutBucketPolicy"],
        "Resource": ["arn:aws:s3:::bucket", "arn:aws:s3:::bucket/*"]
        }}""",
            ignore_private_auditors=True,
        )
        assert_equal(
            policy.finding_ids,
            set(["RESOURCE_POLICY_PRIVILEGE_ESCALATION"]),
            "Resource policy privilege escalation",
        )

        policy = analyze_policy_string(
            """{
    "Version": "2012-10-17",
    "Statement": [{
        "Effect": "Allow",
        "Action": ["s3:*Bucket*", "s3:*Object*"],
        "Resource": ["arn:aws:s3:::bucket1", "arn:aws:s3:::bucket1/*"]
        },
        {
        "Effect": "Allow",
        "Action": ["s3:*Object"],
        "Resource": ["arn:aws:s3:::bucket2/*"]
        }]}""",
            ignore_private_auditors=True,
        )
        # There is one finding for "No resources match for s3:ListAllMyBuckets which requires a resource format of *"
        assert_equal(
            policy.finding_ids,
            set(["RESOURCE_MISMATCH"]),
            "Buckets do not match so no escalation possible",
        )
예제 #25
0
 def test_condition_type_unqoted_bool(self):
     policy = analyze_policy_string(
         """{
 "Version": "2012-10-17",
 "Statement": {
     "Effect": "Allow",
     "Action": "kms:CreateGrant",
     "Resource": "*",
     "Condition": {"Bool": {"kms:GrantIsForAWSResource": true}} }}""",
         ignore_private_auditors=True,
     )
     assert_equal(
         policy.finding_ids, set(["RESOURCE_STAR"]),
     )
예제 #26
0
    def test_resource_with_sub(self):
        policy = analyze_policy_string("""{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Sid":"AddPerm",
      "Effect":"Allow",
      "Principal": "*",
      "Action":["ssm:PutParameter"],
      "Resource":[{"Fn::Sub": "arn:aws:ssm:*:${AWS::AccountId}:*"}]
    }
  ]
}""")
        assert_equal(policy.finding_ids, {"INVALID_ARN"})
예제 #27
0
 def test_analyze_policy_string_multiple_statements_one_bad(self):
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": [{
     "Effect": "Allow",
     "Action": "s3:listallmybuckets",
     "Resource": "*"},
     {
     "Effect": "Allow",
     "Action": ["iam:listusers", "iam:list"],
     "Resource": "*"}]}""")
     assert_false(
         len(policy.findings) == 0,
         "Policy with multiple statements has one bad")
예제 #28
0
 def test_analyze_policy_string_MFA_formatting(self):
     policy = analyze_policy_string("""{
 "Version": "2012-10-17",
 "Statement": {
         "Sid": "AllowManageOwnVirtualMFADevice",
         "Effect": "Allow",
         "Action": [
             "iam:CreateVirtualMFADevice",
             "iam:DeleteVirtualMFADevice"
         ],
         "Resource": "arn:aws:iam::*:mfa/${aws:username}"
     }
     }""")
     assert_equal(policy.finding_ids, set([]), "Policy is valid")
예제 #29
0
    def test_condition_action_specific(self):
        policy = analyze_policy_string("""{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "s3:listbucket",
        "Resource": "arn:aws:s3:::bucket-name",
        "Condition": {"StringEquals": {"s3:prefix":["home/${aws:username}/*"]}} }}"""
                                       )
        assert_equal(len(policy.findings), 0)

        # The key s3:x-amz-storage-class is not allowed for ListBucket,
        # but is for other S3 actions
        policy = analyze_policy_string("""{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "s3:listbucket",
        "Resource": "arn:aws:s3:::bucket-name",
        "Condition": {"StringEquals": {"s3:x-amz-storage-class":"bad"}} }}""")
        assert_false(
            len(policy.findings) == 0,
            "Policy uses key that cannot be used for the action",
        )
예제 #30
0
    def test_bad_principals(self):
        # Good principal
        policy = analyze_policy_string("""{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Sid":"AddPerm",
      "Effect":"Allow",
      "Principal": "*",
      "Action":["s3:GetObject"],
      "Resource":["arn:aws:s3:::examplebucket/*"]
    }
  ]
}""")
        assert_true(len(policy.findings) == 0, "Basic S3 bucket policy")