mirror of
https://github.com/prowler-cloud/prowler.git
synced 2025-12-19 05:17:47 +00:00
Compare commits
4 Commits
0d0dabe166
...
PRWLR-4539
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2681acf996 | ||
|
|
062a53b2ad | ||
|
|
7fcee422c8 | ||
|
|
f6f204fd6b |
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "iam_policy_no_kms_decryption_actions",
|
||||
"CheckTitle": "Check if IAM customer managed policies",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/CIS AWS Foundations Benchmark"
|
||||
],
|
||||
"ServiceName": "iam",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:aws:iam::{account-id}:{resource-type}/{resource-id}",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsIamPolicy",
|
||||
"Description": "Ensure IAM customer managed policies do not allow broad decryption permissions (kms:Decrypt or kms:ReEncryptFrom) on all AWS KMS keys",
|
||||
"Risk": "Allowing broad decryption permissions (kms:Decrypt or kms:ReEncryptFrom) on all AWS KMS keys can lead to unauthorized data access, especially if users inadvertently or maliciously decrypt sensitive information.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/kms/latest/developerguide/iam-policies.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws iam update-policy --policy-arn <policy-arn> --policy-document '{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Action\": [ \"kms:Decrypt\", \"kms:ReEncryptFrom\" ], \"Resource\": \"arn:aws:kms:<region>:<account-id>:key/<key-id>\" } ] }'",
|
||||
"NativeIaC": "",
|
||||
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/kms-controls.html#kms-1",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Restrict decryption actions in IAM customer managed policies to specific KMS keys rather than allowing permissions on all keys. By specifying particular key ARNs in the Resource field, you limit the keys that identities can decrypt, enhancing control and compliance with best practices in data security.",
|
||||
"Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-edit.html#edit-managed-policy-console"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
from typing import List
|
||||
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
critical_service = "kms"
|
||||
|
||||
|
||||
class iam_policy_no_kms_decryption_actions(Check):
|
||||
"""Check IAM policies for KMS decryption actions.
|
||||
|
||||
This class verifies that IAM policies do not allow KMS decryption actions (kms:Decrypt or kms:ReEncryptFrom) on all resources.
|
||||
"""
|
||||
|
||||
def execute(self) -> List[Check_Report_AWS]:
|
||||
"""Execute the No KMS Decryption Actions check.
|
||||
|
||||
Iterates over all IAM policies and checks if any of them allow KMS decryption actions.
|
||||
|
||||
Returns:
|
||||
List[Check_Report_AWS]: A list of reports for each IAM policy that allows KMS decryption actions.
|
||||
"""
|
||||
findings = []
|
||||
for policy in iam_client.policies:
|
||||
# Check only custom policies
|
||||
if policy.type == "Custom":
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = policy.arn
|
||||
report.resource_id = policy.name
|
||||
report.resource_tags = policy.tags
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Custom Policy {policy.name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
|
||||
|
||||
if policy.document:
|
||||
for statement in policy.document.get("Statement", []):
|
||||
if (
|
||||
statement.get("Effect") == "Allow"
|
||||
and "Action" in statement
|
||||
and (
|
||||
"kms:Decrypt" in statement["Action"]
|
||||
or "kms:ReEncryptFrom" in statement["Action"]
|
||||
)
|
||||
and "Resource" in statement
|
||||
and "*" in statement["Resource"]
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Custom Policy {policy.name} does allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -1,14 +1,13 @@
|
||||
import json
|
||||
from typing import Optional
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
|
||||
from prowler.providers.aws.lib.service.service import AWSService
|
||||
|
||||
|
||||
################## KMS
|
||||
class KMS(AWSService):
|
||||
def __init__(self, provider):
|
||||
# Call AWSService's __init__
|
||||
@@ -115,11 +114,11 @@ class KMS(AWSService):
|
||||
class Key(BaseModel):
|
||||
id: str
|
||||
arn: str
|
||||
state: Optional[str]
|
||||
origin: Optional[str]
|
||||
manager: Optional[str]
|
||||
rotation_enabled: Optional[bool]
|
||||
policy: Optional[dict]
|
||||
spec: Optional[str]
|
||||
state: Optional[str] = None
|
||||
origin: Optional[str] = None
|
||||
manager: Optional[str] = None
|
||||
rotation_enabled: Optional[bool] = None
|
||||
policy: Optional[Dict] = None
|
||||
spec: Optional[str] = None
|
||||
region: str
|
||||
tags: Optional[list] = []
|
||||
tags: List[Dict[str, str]] = Field(default_factory=list)
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
from json import dumps
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_aws
|
||||
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
|
||||
|
||||
|
||||
class Test_iam_policy_no_kms_decryption_actions:
|
||||
@mock_aws
|
||||
def test_no_policies(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
|
||||
iam_policy_no_kms_decryption_actions,
|
||||
)
|
||||
|
||||
check = iam_policy_no_kms_decryption_actions()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_aws
|
||||
def test_iam_policy_no_kms_actions(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
iam_client = client("iam")
|
||||
policy_name = "no_kms_actions"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Deny", "Action": "kms:Decrypt", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
|
||||
iam_policy_no_kms_decryption_actions,
|
||||
)
|
||||
|
||||
check = iam_policy_no_kms_decryption_actions()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Custom Policy {policy_name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
|
||||
)
|
||||
assert result[0].resource_id == policy_name
|
||||
assert result[0].resource_arn == arn
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_iam_policy_allows_kms_decrypt(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
iam_client = client("iam")
|
||||
policy_name = "allows_kms_decrypt"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "kms:Decrypt", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
|
||||
iam_policy_no_kms_decryption_actions,
|
||||
)
|
||||
|
||||
check = iam_policy_no_kms_decryption_actions()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Custom Policy {policy_name} does allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
|
||||
)
|
||||
assert result[0].resource_id == policy_name
|
||||
assert result[0].resource_arn == arn
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_iam_policy_allows_kms_reencrypt_from(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
iam_client = client("iam")
|
||||
policy_name = "allows_kms_reencrypt_from"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "kms:ReEncryptFrom", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
|
||||
iam_policy_no_kms_decryption_actions,
|
||||
)
|
||||
|
||||
check = iam_policy_no_kms_decryption_actions()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Custom Policy {policy_name} does allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
|
||||
)
|
||||
assert result[0].resource_id == policy_name
|
||||
assert result[0].resource_arn == arn
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_iam_policy_allows_kms_decrypt_on_specific_resource(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
iam_client = client("iam")
|
||||
policy_name = "allows_kms_decrypt_specific_resource"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "kms:Decrypt",
|
||||
"Resource": "arn:aws:kms:us-east-1:123456789012:key/1234abcd",
|
||||
},
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
|
||||
iam_policy_no_kms_decryption_actions,
|
||||
)
|
||||
|
||||
check = iam_policy_no_kms_decryption_actions()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Custom Policy {policy_name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
|
||||
)
|
||||
assert result[0].resource_id == policy_name
|
||||
assert result[0].resource_arn == arn
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_iam_policy_allows_kms_reencrypt_from_on_specific_resource(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
iam_client = client("iam")
|
||||
policy_name = "allows_kms_decrypt_specific_resource"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "kms:ReEncryptFrom",
|
||||
"Resource": "arn:aws:kms:us-east-1:123456789012:key/1234abcd",
|
||||
},
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
|
||||
iam_policy_no_kms_decryption_actions,
|
||||
)
|
||||
|
||||
check = iam_policy_no_kms_decryption_actions()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Custom Policy {policy_name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
|
||||
)
|
||||
assert result[0].resource_id == policy_name
|
||||
assert result[0].resource_arn == arn
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_iam_policy_allows_both_kms_actions(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
iam_client = client("iam")
|
||||
policy_name = "allows_both_kms_actions"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": ["kms:Decrypt", "kms:ReEncryptFrom"],
|
||||
"Resource": "*",
|
||||
},
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
|
||||
iam_policy_no_kms_decryption_actions,
|
||||
)
|
||||
|
||||
check = iam_policy_no_kms_decryption_actions()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Custom Policy {policy_name} does allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
|
||||
)
|
||||
assert result[0].resource_id == policy_name
|
||||
assert result[0].resource_arn == arn
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_iam_policy_unrelated_actions_policy(self):
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
iam_client = client("iam")
|
||||
policy_name = "unrelated_actions_policy"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "s3:ListBucket", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.iam.iam_policy_no_kms_decryption_actions.iam_policy_no_kms_decryption_actions import (
|
||||
iam_policy_no_kms_decryption_actions,
|
||||
)
|
||||
|
||||
check = iam_policy_no_kms_decryption_actions()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Custom Policy {policy_name} does not allow kms:Decrypt or kms:ReEncryptFrom privileges on all resources."
|
||||
)
|
||||
assert result[0].resource_id == policy_name
|
||||
assert result[0].resource_arn == arn
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_tags == []
|
||||
Reference in New Issue
Block a user