mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-05 03:12:14 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2df83e1fea | ||
|
|
845d555fc9 |
@@ -0,0 +1 @@
|
||||
`cloudtrail_threat_detection_enumeration`, `cloudtrail_threat_detection_privilege_escalation`, and `cloudtrail_threat_detection_llm_jacking` checks with stable principal resource identities and consistent metadata
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
from botocore.client import ClientError
|
||||
from pydantic.v1 import BaseModel
|
||||
@@ -197,10 +198,10 @@ class Cloudtrail(AWSService):
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def _lookup_events(self, trail, event_name, minutes):
|
||||
def _lookup_events(self, trail, event_name, minutes, region=None):
|
||||
logger.info("CloudTrail - Lookup Events...")
|
||||
try:
|
||||
regional_client = self.regional_clients[trail.region]
|
||||
regional_client = self.regional_clients[region or trail.region]
|
||||
response = regional_client.lookup_events(
|
||||
LookupAttributes=[
|
||||
{"AttributeKey": "EventName", "AttributeValue": event_name}
|
||||
@@ -294,3 +295,103 @@ class Trail(BaseModel):
|
||||
data_events: list[Event_Selector] = []
|
||||
tags: Optional[list] = []
|
||||
has_insight_selectors: str = None
|
||||
|
||||
|
||||
class CloudTrailThreatDetectionResource(BaseModel):
|
||||
"""Normalized AWS identity resource for CloudTrail threat-detection findings."""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
arn: str
|
||||
region: str
|
||||
identity_type: str
|
||||
source_arn: str
|
||||
|
||||
|
||||
def normalize_cloudtrail_identity(
|
||||
user_identity: dict, region: str
|
||||
) -> Optional[CloudTrailThreatDetectionResource]:
|
||||
source_arn = user_identity.get("arn")
|
||||
if not source_arn:
|
||||
return None
|
||||
|
||||
identity_type = user_identity.get("type", "Unknown")
|
||||
identity_arn = source_arn
|
||||
if identity_type == "AssumedRole":
|
||||
identity_arn = (
|
||||
user_identity.get("sessionContext", {}).get("sessionIssuer", {}).get("arn")
|
||||
or source_arn
|
||||
)
|
||||
|
||||
resource_component = identity_arn.split(":", 5)[-1]
|
||||
name = resource_component.rsplit("/", 1)[-1]
|
||||
if identity_type == "AssumedRole" and identity_arn == source_arn:
|
||||
name = resource_component.removeprefix("assumed-role/").split("/", 1)[0]
|
||||
|
||||
return CloudTrailThreatDetectionResource(
|
||||
id=resource_component,
|
||||
name=name,
|
||||
arn=identity_arn,
|
||||
region=region,
|
||||
identity_type=identity_type,
|
||||
source_arn=source_arn,
|
||||
)
|
||||
|
||||
|
||||
def get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client: Any, actions: list[str], minutes: int
|
||||
) -> Optional[dict[str, tuple[CloudTrailThreatDetectionResource, set[str]]]]:
|
||||
identities = {}
|
||||
if cloudtrail_client.trails is None:
|
||||
return None
|
||||
|
||||
multiregion_trail = next(
|
||||
(trail for trail in cloudtrail_client.trails.values() if trail.is_multiregion),
|
||||
None,
|
||||
)
|
||||
trails_to_scan = (
|
||||
[multiregion_trail] if multiregion_trail else cloudtrail_client.trails.values()
|
||||
)
|
||||
|
||||
for trail in trails_to_scan:
|
||||
regions = (
|
||||
cloudtrail_client.regional_clients
|
||||
if trail.is_multiregion
|
||||
else [trail.region]
|
||||
)
|
||||
for action in actions:
|
||||
for region in regions:
|
||||
lookup_arguments = {
|
||||
"trail": trail,
|
||||
"event_name": action,
|
||||
"minutes": minutes,
|
||||
}
|
||||
if trail.is_multiregion:
|
||||
lookup_arguments["region"] = region
|
||||
event_logs = cloudtrail_client._lookup_events(**lookup_arguments)
|
||||
if event_logs is None:
|
||||
return None
|
||||
for event_log in event_logs:
|
||||
event = json.loads(event_log["CloudTrailEvent"])
|
||||
resource = normalize_cloudtrail_identity(
|
||||
event.get("userIdentity", {}), cloudtrail_client.region
|
||||
)
|
||||
if resource:
|
||||
identities.setdefault(resource.arn, (resource, set()))[1].add(
|
||||
action
|
||||
)
|
||||
|
||||
return identities
|
||||
|
||||
|
||||
def get_cloudtrail_account_resource(
|
||||
account_id: str, account_arn: str, region: str
|
||||
) -> CloudTrailThreatDetectionResource:
|
||||
return CloudTrailThreatDetectionResource(
|
||||
id=account_id,
|
||||
name=account_id,
|
||||
arn=account_arn,
|
||||
region=region,
|
||||
identity_type="AWSAccount",
|
||||
source_arn=account_arn,
|
||||
)
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"ResourceType": "Other",
|
||||
"ResourceGroup": "monitoring",
|
||||
"Description": "**CloudTrail activity** is analyzed for AWS identities executing a broad mix of discovery APIs like `List*`, `Describe*`, and `Get*` within a recent time window.\n\nAn identity exceeding a configurable ratio of these actions indicates potential enumeration behavior by that principal.",
|
||||
"Risk": "Concentrated discovery activity signals **reconnaissance** with valid credentials. Adversaries can map assets and policies to enable **privilege escalation**, target data stores for **exfiltration** (confidentiality), and identify services to disrupt (availability), supporting stealthy lateral movement.",
|
||||
|
||||
+33
-58
@@ -1,9 +1,11 @@
|
||||
import json
|
||||
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
cloudtrail_client,
|
||||
)
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
get_cloudtrail_account_resource,
|
||||
get_cloudtrail_threat_detection_identities,
|
||||
)
|
||||
|
||||
default_threat_detection_enumeration_actions = [
|
||||
"CreateIndex",
|
||||
@@ -102,7 +104,14 @@ default_threat_detection_enumeration_actions = [
|
||||
|
||||
|
||||
class cloudtrail_threat_detection_enumeration(Check):
|
||||
def execute(self):
|
||||
"""Detect potential AWS API enumeration activity recorded by CloudTrail."""
|
||||
|
||||
def execute(self) -> list[Check_Report_AWS]:
|
||||
"""Evaluate CloudTrail events for potential enumeration activity.
|
||||
|
||||
Returns:
|
||||
list[Check_Report_AWS]: Reports for detected identities or the account.
|
||||
"""
|
||||
findings = []
|
||||
threshold = cloudtrail_client.audit_config.get(
|
||||
"threat_detection_enumeration_threshold", 0.3
|
||||
@@ -114,71 +123,37 @@ class cloudtrail_threat_detection_enumeration(Check):
|
||||
"threat_detection_enumeration_actions",
|
||||
default_threat_detection_enumeration_actions,
|
||||
)
|
||||
potential_enumeration = {}
|
||||
found_potential_enumeration = False
|
||||
multiregion_trail = None
|
||||
# Check if any trail is multi-region so we only need to check once
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_multiregion:
|
||||
multiregion_trail = trail
|
||||
break
|
||||
trails_to_scan = (
|
||||
cloudtrail_client.trails.values()
|
||||
if not multiregion_trail
|
||||
else [multiregion_trail]
|
||||
potential_enumeration = get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client, enumeration_actions, threat_detection_minutes
|
||||
)
|
||||
for trail in trails_to_scan:
|
||||
for event_name in enumeration_actions:
|
||||
for event_log in cloudtrail_client._lookup_events(
|
||||
trail=trail,
|
||||
event_name=event_name,
|
||||
minutes=threat_detection_minutes,
|
||||
):
|
||||
event_log = json.loads(event_log["CloudTrailEvent"])
|
||||
if (
|
||||
"arn" in event_log["userIdentity"]
|
||||
): # Ignore event logs without ARN since they are AWS services
|
||||
if (
|
||||
event_log["userIdentity"]["arn"],
|
||||
event_log["userIdentity"]["type"],
|
||||
) not in potential_enumeration:
|
||||
potential_enumeration[
|
||||
(
|
||||
event_log["userIdentity"]["arn"],
|
||||
event_log["userIdentity"]["type"],
|
||||
)
|
||||
] = set()
|
||||
potential_enumeration[
|
||||
(
|
||||
event_log["userIdentity"]["arn"],
|
||||
event_log["userIdentity"]["type"],
|
||||
)
|
||||
].add(event_name)
|
||||
|
||||
for aws_identity, actions in potential_enumeration.items():
|
||||
if potential_enumeration is None:
|
||||
resource = get_cloudtrail_account_resource(
|
||||
cloudtrail_client.audited_account,
|
||||
cloudtrail_client.audited_account_arn,
|
||||
cloudtrail_client.region,
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=resource)
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = "Cannot evaluate CloudTrail threat detection because CloudTrail trails or events could not be retrieved in at least one audited region. Verify that the scanning credentials are allowed to call cloudtrail:DescribeTrails and cloudtrail:LookupEvents."
|
||||
return [report]
|
||||
|
||||
for resource, actions in potential_enumeration.values():
|
||||
identity_threshold = round(len(actions) / len(enumeration_actions), 2)
|
||||
aws_identity_type = aws_identity[1]
|
||||
aws_identity_arn = aws_identity[0]
|
||||
if len(actions) / len(enumeration_actions) > threshold:
|
||||
found_potential_enumeration = True
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = aws_identity_arn.split("/")[-1]
|
||||
report.resource_arn = aws_identity_arn
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=resource)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Potential enumeration attack detected from AWS {aws_identity_type} {aws_identity_arn.split('/')[-1]} with a threshold of {identity_threshold}."
|
||||
report.status_extended = f"Potential enumeration attack detected from AWS {resource.identity_type} {resource.name} with a threshold of {identity_threshold}."
|
||||
findings.append(report)
|
||||
if not found_potential_enumeration:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.resource_arn = cloudtrail_client._get_trail_arn_template(
|
||||
cloudtrail_client.region
|
||||
resource = get_cloudtrail_account_resource(
|
||||
cloudtrail_client.audited_account,
|
||||
cloudtrail_client.audited_account_arn,
|
||||
cloudtrail_client.region,
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=resource)
|
||||
report.status = "PASS"
|
||||
report.status_extended = "No potential enumeration attack detected."
|
||||
findings.append(report)
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"ResourceType": "Other",
|
||||
"ResourceGroup": "monitoring",
|
||||
"Description": "**CloudTrail Bedrock activity** is analyzed per identity for a high diversity of LLM-related API calls (e.g., `InvokeModel`, `InvokeModelWithResponseStream`, `GetFoundationModelAvailability`). *If an identity's share of these actions exceeds a configured threshold over a recent window*, it is surfaced as potential **LLM-jacking** behavior.",
|
||||
"Risk": "Such patterns suggest **stolen credential** abuse to drive LLM usage.\n- Availability: cost exhaustion and service disruption\n- Confidentiality: leakage of prompts/outputs and model settings\n- Integrity: misuse of permissions for broader access\nAttackers may use reverse proxies to resell access and obfuscate sources.",
|
||||
|
||||
+33
-58
@@ -1,9 +1,11 @@
|
||||
import json
|
||||
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
cloudtrail_client,
|
||||
)
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
get_cloudtrail_account_resource,
|
||||
get_cloudtrail_threat_detection_identities,
|
||||
)
|
||||
|
||||
default_threat_detection_llm_jacking_actions = [
|
||||
"PutUseCaseForModelAccess",
|
||||
@@ -24,7 +26,14 @@ default_threat_detection_llm_jacking_actions = [
|
||||
|
||||
|
||||
class cloudtrail_threat_detection_llm_jacking(Check):
|
||||
def execute(self):
|
||||
"""Detect potential LLM jacking activity recorded by CloudTrail."""
|
||||
|
||||
def execute(self) -> list[Check_Report_AWS]:
|
||||
"""Evaluate CloudTrail events for potential LLM jacking activity.
|
||||
|
||||
Returns:
|
||||
list[Check_Report_AWS]: Reports for detected identities or the account.
|
||||
"""
|
||||
findings = []
|
||||
threshold = cloudtrail_client.audit_config.get(
|
||||
"threat_detection_llm_jacking_threshold", 0.4
|
||||
@@ -36,71 +45,37 @@ class cloudtrail_threat_detection_llm_jacking(Check):
|
||||
"threat_detection_llm_jacking_actions",
|
||||
default_threat_detection_llm_jacking_actions,
|
||||
)
|
||||
potential_llm_jacking = {}
|
||||
found_potential_llm_jacking = False
|
||||
multiregion_trail = None
|
||||
# Check if any trail is multi-region so we only need to check once
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_multiregion:
|
||||
multiregion_trail = trail
|
||||
break
|
||||
trails_to_scan = (
|
||||
cloudtrail_client.trails.values()
|
||||
if not multiregion_trail
|
||||
else [multiregion_trail]
|
||||
potential_llm_jacking = get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client, llm_jacking_actions, threat_detection_minutes
|
||||
)
|
||||
for trail in trails_to_scan:
|
||||
for event_name in llm_jacking_actions:
|
||||
for event_log in cloudtrail_client._lookup_events(
|
||||
trail=trail,
|
||||
event_name=event_name,
|
||||
minutes=threat_detection_minutes,
|
||||
):
|
||||
event_log = json.loads(event_log["CloudTrailEvent"])
|
||||
if (
|
||||
"arn" in event_log["userIdentity"]
|
||||
): # Ignore event logs without ARN since they are AWS services
|
||||
if (
|
||||
event_log["userIdentity"]["arn"],
|
||||
event_log["userIdentity"]["type"],
|
||||
) not in potential_llm_jacking:
|
||||
potential_llm_jacking[
|
||||
(
|
||||
event_log["userIdentity"]["arn"],
|
||||
event_log["userIdentity"]["type"],
|
||||
)
|
||||
] = set()
|
||||
potential_llm_jacking[
|
||||
(
|
||||
event_log["userIdentity"]["arn"],
|
||||
event_log["userIdentity"]["type"],
|
||||
)
|
||||
].add(event_name)
|
||||
|
||||
for aws_identity, actions in potential_llm_jacking.items():
|
||||
if potential_llm_jacking is None:
|
||||
resource = get_cloudtrail_account_resource(
|
||||
cloudtrail_client.audited_account,
|
||||
cloudtrail_client.audited_account_arn,
|
||||
cloudtrail_client.region,
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=resource)
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = "Cannot evaluate CloudTrail threat detection because CloudTrail trails or events could not be retrieved in at least one audited region. Verify that the scanning credentials are allowed to call cloudtrail:DescribeTrails and cloudtrail:LookupEvents."
|
||||
return [report]
|
||||
|
||||
for resource, actions in potential_llm_jacking.values():
|
||||
identity_threshold = round(len(actions) / len(llm_jacking_actions), 2)
|
||||
aws_identity_type = aws_identity[1]
|
||||
aws_identity_arn = aws_identity[0]
|
||||
if len(actions) / len(llm_jacking_actions) > threshold:
|
||||
found_potential_llm_jacking = True
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = aws_identity_arn.split("/")[-1]
|
||||
report.resource_arn = aws_identity_arn
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=resource)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Potential LLM Jacking attack detected from AWS {aws_identity_type} {aws_identity_arn.split('/')[-1]} with a threshold of {identity_threshold}."
|
||||
report.status_extended = f"Potential LLM Jacking attack detected from AWS {resource.identity_type} {resource.name} with a threshold of {identity_threshold}."
|
||||
findings.append(report)
|
||||
if not found_potential_llm_jacking:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.resource_arn = cloudtrail_client._get_trail_arn_template(
|
||||
cloudtrail_client.region
|
||||
resource = get_cloudtrail_account_resource(
|
||||
cloudtrail_client.audited_account,
|
||||
cloudtrail_client.audited_account_arn,
|
||||
cloudtrail_client.region,
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=resource)
|
||||
report.status = "PASS"
|
||||
report.status_extended = "No potential LLM Jacking attack detected."
|
||||
findings.append(report)
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "AwsCloudTrailTrail",
|
||||
"ResourceType": "Other",
|
||||
"ResourceGroup": "monitoring",
|
||||
"Description": "**CloudTrail** activity is analyzed for **identities** executing high-risk actions linked to **privilege escalation** (e.g., `Attach*Policy`, `PassRole`, `AssumeRole`, `CreateAccessKey`). Identities exceeding a configurable share of such events within a *recent time window* are highlighted for investigation.",
|
||||
"Risk": "Escalation patterns can grant elevated entitlements, enabling:\n- Confidentiality loss via unauthorized data/secret access\n- Integrity compromise by changing IAM policies/roles\n- Availability impact by tampering with logging or resources\nThis also facilitates lateral movement and persistence.",
|
||||
|
||||
+34
-58
@@ -1,9 +1,11 @@
|
||||
import json
|
||||
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
cloudtrail_client,
|
||||
)
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
get_cloudtrail_account_resource,
|
||||
get_cloudtrail_threat_detection_identities,
|
||||
)
|
||||
|
||||
default_threat_detection_privilege_escalation_actions = [
|
||||
"AddPermission",
|
||||
@@ -61,7 +63,14 @@ default_threat_detection_privilege_escalation_actions = [
|
||||
|
||||
|
||||
class cloudtrail_threat_detection_privilege_escalation(Check):
|
||||
def execute(self):
|
||||
"""Detect potential privilege-escalation activity recorded by CloudTrail."""
|
||||
|
||||
def execute(self) -> list[Check_Report_AWS]:
|
||||
"""Evaluate CloudTrail events for potential privilege-escalation activity.
|
||||
|
||||
Returns:
|
||||
list[Check_Report_AWS]: Reports for detected identities or the account.
|
||||
"""
|
||||
findings = []
|
||||
threshold = cloudtrail_client.audit_config.get(
|
||||
"threat_detection_privilege_escalation_threshold", 0.2
|
||||
@@ -74,72 +83,39 @@ class cloudtrail_threat_detection_privilege_escalation(Check):
|
||||
default_threat_detection_privilege_escalation_actions,
|
||||
)
|
||||
|
||||
potential_privilege_escalation = {}
|
||||
found_potential_privilege_escalation = False
|
||||
multiregion_trail = None
|
||||
# Check if any trail is multi-region so we only need to check once
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.is_multiregion:
|
||||
multiregion_trail = trail
|
||||
break
|
||||
trails_to_scan = (
|
||||
cloudtrail_client.trails.values()
|
||||
if not multiregion_trail
|
||||
else [multiregion_trail]
|
||||
potential_privilege_escalation = get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client,
|
||||
privilege_escalation_actions,
|
||||
threat_detection_minutes,
|
||||
)
|
||||
for trail in trails_to_scan:
|
||||
for event_name in privilege_escalation_actions:
|
||||
for event_log in cloudtrail_client._lookup_events(
|
||||
trail=trail,
|
||||
event_name=event_name,
|
||||
minutes=threat_detection_minutes,
|
||||
):
|
||||
event_log = json.loads(event_log["CloudTrailEvent"])
|
||||
if (
|
||||
"arn" in event_log["userIdentity"]
|
||||
): # Ignore event logs without ARN since they are AWS services
|
||||
if (
|
||||
event_log["userIdentity"]["arn"],
|
||||
event_log["userIdentity"]["type"],
|
||||
) not in potential_privilege_escalation:
|
||||
potential_privilege_escalation[
|
||||
(
|
||||
event_log["userIdentity"]["arn"],
|
||||
event_log["userIdentity"]["type"],
|
||||
)
|
||||
] = set()
|
||||
potential_privilege_escalation[
|
||||
(
|
||||
event_log["userIdentity"]["arn"],
|
||||
event_log["userIdentity"]["type"],
|
||||
)
|
||||
].add(event_name)
|
||||
for aws_identity, actions in potential_privilege_escalation.items():
|
||||
if potential_privilege_escalation is None:
|
||||
resource = get_cloudtrail_account_resource(
|
||||
cloudtrail_client.audited_account,
|
||||
cloudtrail_client.audited_account_arn,
|
||||
cloudtrail_client.region,
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=resource)
|
||||
report.status = "MANUAL"
|
||||
report.status_extended = "Cannot evaluate CloudTrail threat detection because CloudTrail trails or events could not be retrieved in at least one audited region. Verify that the scanning credentials are allowed to call cloudtrail:DescribeTrails and cloudtrail:LookupEvents."
|
||||
return [report]
|
||||
for resource, actions in potential_privilege_escalation.values():
|
||||
identity_threshold = round(
|
||||
len(actions) / len(privilege_escalation_actions), 2
|
||||
)
|
||||
aws_identity_type = aws_identity[1]
|
||||
aws_identity_arn = aws_identity[0]
|
||||
if len(actions) / len(privilege_escalation_actions) > threshold:
|
||||
found_potential_privilege_escalation = True
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = aws_identity_arn.split("/")[-1]
|
||||
report.resource_arn = aws_identity_arn
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=resource)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Potential privilege escalation attack detected from AWS {aws_identity_type} {aws_identity_arn.split('/')[-1]} with a threshold of {identity_threshold}."
|
||||
report.status_extended = f"Potential privilege escalation attack detected from AWS {resource.identity_type} {resource.name} with a threshold of {identity_threshold}."
|
||||
findings.append(report)
|
||||
if not found_potential_privilege_escalation:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(), resource=cloudtrail_client.trails
|
||||
)
|
||||
report.region = cloudtrail_client.region
|
||||
report.resource_id = cloudtrail_client.audited_account
|
||||
report.resource_arn = cloudtrail_client._get_trail_arn_template(
|
||||
cloudtrail_client.region
|
||||
resource = get_cloudtrail_account_resource(
|
||||
cloudtrail_client.audited_account,
|
||||
cloudtrail_client.audited_account_arn,
|
||||
cloudtrail_client.region,
|
||||
)
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=resource)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
"No potential privilege escalation attack detected."
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_aws
|
||||
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
Cloudtrail,
|
||||
get_cloudtrail_threat_detection_identities,
|
||||
normalize_cloudtrail_identity,
|
||||
)
|
||||
from tests.providers.aws.utils import (
|
||||
AWS_ACCOUNT_NUMBER,
|
||||
AWS_REGION_EU_SOUTH_2,
|
||||
@@ -343,3 +351,312 @@ class Test_Cloudtrail_Service:
|
||||
if trail.name:
|
||||
if trail.name == trail_name_us:
|
||||
assert trail.tags == [{"Key": "test", "Value": tag}]
|
||||
|
||||
|
||||
class Test_normalize_cloudtrail_identity:
|
||||
def test_iam_user_with_path(self):
|
||||
identity_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/engineering/platform/attacker"
|
||||
)
|
||||
|
||||
resource = normalize_cloudtrail_identity(
|
||||
{"type": "IAMUser", "arn": identity_arn}, AWS_REGION_US_EAST_1
|
||||
)
|
||||
|
||||
assert resource.id == "user/engineering/platform/attacker"
|
||||
assert resource.name == "attacker"
|
||||
assert resource.arn == identity_arn
|
||||
assert resource.region == AWS_REGION_US_EAST_1
|
||||
assert resource.identity_type == "IAMUser"
|
||||
assert resource.source_arn == identity_arn
|
||||
|
||||
def test_assumed_role_with_session_issuer(self):
|
||||
source_arn = (
|
||||
f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:assumed-role/platform/admin/session-one"
|
||||
)
|
||||
role_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/platform/admin"
|
||||
|
||||
resource = normalize_cloudtrail_identity(
|
||||
{
|
||||
"type": "AssumedRole",
|
||||
"arn": source_arn,
|
||||
"sessionContext": {"sessionIssuer": {"arn": role_arn}},
|
||||
},
|
||||
AWS_REGION_US_EAST_1,
|
||||
)
|
||||
|
||||
assert resource.id == "role/platform/admin"
|
||||
assert resource.name == "admin"
|
||||
assert resource.arn == role_arn
|
||||
assert resource.identity_type == "AssumedRole"
|
||||
assert resource.source_arn == source_arn
|
||||
|
||||
def test_assumed_role_without_session_issuer(self):
|
||||
source_arn = (
|
||||
f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:assumed-role/platform-admin/session-one"
|
||||
)
|
||||
|
||||
resource = normalize_cloudtrail_identity(
|
||||
{"type": "AssumedRole", "arn": source_arn}, AWS_REGION_US_EAST_1
|
||||
)
|
||||
|
||||
assert resource.id == "assumed-role/platform-admin/session-one"
|
||||
assert resource.name == "platform-admin"
|
||||
assert resource.arn == source_arn
|
||||
assert resource.source_arn == source_arn
|
||||
|
||||
def test_user_and_role_with_same_leaf_name_are_distinct(self):
|
||||
user = normalize_cloudtrail_identity(
|
||||
{
|
||||
"type": "IAMUser",
|
||||
"arn": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/team/operator",
|
||||
},
|
||||
AWS_REGION_US_EAST_1,
|
||||
)
|
||||
role = normalize_cloudtrail_identity(
|
||||
{
|
||||
"type": "AssumedRole",
|
||||
"arn": f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:assumed-role/operator/session",
|
||||
"sessionContext": {
|
||||
"sessionIssuer": {
|
||||
"arn": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/team/operator"
|
||||
}
|
||||
},
|
||||
},
|
||||
AWS_REGION_US_EAST_1,
|
||||
)
|
||||
|
||||
assert user.id == "user/team/operator"
|
||||
assert role.id == "role/team/operator"
|
||||
|
||||
def test_roles_with_same_session_name_are_distinct(self):
|
||||
first_role = normalize_cloudtrail_identity(
|
||||
{
|
||||
"type": "AssumedRole",
|
||||
"arn": f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:assumed-role/first/shared-session",
|
||||
"sessionContext": {
|
||||
"sessionIssuer": {
|
||||
"arn": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/first"
|
||||
}
|
||||
},
|
||||
},
|
||||
AWS_REGION_US_EAST_1,
|
||||
)
|
||||
second_role = normalize_cloudtrail_identity(
|
||||
{
|
||||
"type": "AssumedRole",
|
||||
"arn": f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:assumed-role/second/shared-session",
|
||||
"sessionContext": {
|
||||
"sessionIssuer": {
|
||||
"arn": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/second"
|
||||
}
|
||||
},
|
||||
},
|
||||
AWS_REGION_US_EAST_1,
|
||||
)
|
||||
|
||||
assert first_role.id == "role/first"
|
||||
assert second_role.id == "role/second"
|
||||
|
||||
def test_federated_user(self):
|
||||
identity_arn = f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:federated-user/external-user"
|
||||
|
||||
resource = normalize_cloudtrail_identity(
|
||||
{"type": "FederatedUser", "arn": identity_arn},
|
||||
AWS_REGION_US_EAST_1,
|
||||
)
|
||||
|
||||
assert resource.id == "federated-user/external-user"
|
||||
assert resource.name == "external-user"
|
||||
assert resource.arn == identity_arn
|
||||
|
||||
def test_root(self):
|
||||
identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
resource = normalize_cloudtrail_identity(
|
||||
{"type": "Root", "arn": identity_arn}, AWS_REGION_US_EAST_1
|
||||
)
|
||||
|
||||
assert resource.id == "root"
|
||||
assert resource.name == "root"
|
||||
assert resource.arn == identity_arn
|
||||
|
||||
def test_unknown_identity_with_arn(self):
|
||||
identity_arn = (
|
||||
f"arn:aws:custom:us-east-1:{AWS_ACCOUNT_NUMBER}:resource/path/name"
|
||||
)
|
||||
|
||||
resource = normalize_cloudtrail_identity(
|
||||
{"type": "UnknownType", "arn": identity_arn}, AWS_REGION_US_EAST_1
|
||||
)
|
||||
|
||||
assert resource.id == "resource/path/name"
|
||||
assert resource.name == "name"
|
||||
assert resource.arn == identity_arn
|
||||
|
||||
def test_identity_without_arn_is_ignored(self):
|
||||
assert (
|
||||
normalize_cloudtrail_identity({"type": "AWSService"}, AWS_REGION_US_EAST_1)
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
class Test_get_cloudtrail_threat_detection_identities:
|
||||
def test_unavailable_trail_inventory_returns_incomplete_visibility(self):
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.trails = None
|
||||
|
||||
assert (
|
||||
get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client, ["ActionOne"], 60
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
def test_failed_event_lookup_returns_incomplete_visibility(self):
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.trails = {
|
||||
"trail": SimpleNamespace(is_multiregion=False, region=AWS_REGION_US_EAST_1)
|
||||
}
|
||||
cloudtrail_client._lookup_events.return_value = None
|
||||
|
||||
assert (
|
||||
get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client, ["ActionOne"], 60
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
def test_empty_event_lookup_returns_complete_empty_result(self):
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.trails = {
|
||||
"trail": SimpleNamespace(is_multiregion=False, region=AWS_REGION_US_EAST_1)
|
||||
}
|
||||
cloudtrail_client._lookup_events.return_value = []
|
||||
|
||||
assert (
|
||||
get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client, ["ActionOne"], 60
|
||||
)
|
||||
== {}
|
||||
)
|
||||
|
||||
def test_multiregion_trail_queries_every_audited_region(self):
|
||||
identity_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/Attacker"
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.regional_clients = {
|
||||
AWS_REGION_US_EAST_1: mock.MagicMock(),
|
||||
AWS_REGION_EU_WEST_1: mock.MagicMock(),
|
||||
}
|
||||
trail = SimpleNamespace(
|
||||
arn=f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail/multiregion",
|
||||
is_multiregion=True,
|
||||
region=AWS_REGION_US_EAST_1,
|
||||
)
|
||||
cloudtrail_client.trails = {trail.arn: trail}
|
||||
|
||||
def lookup_events(trail, event_name, minutes, region=None):
|
||||
del trail, event_name, minutes
|
||||
if region != AWS_REGION_EU_WEST_1:
|
||||
return []
|
||||
return [
|
||||
{
|
||||
"CloudTrailEvent": json.dumps(
|
||||
{
|
||||
"userIdentity": {
|
||||
"type": "IAMUser",
|
||||
"arn": identity_arn,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
cloudtrail_client._lookup_events.side_effect = lookup_events
|
||||
|
||||
identities = get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client, ["ActionOne"], 60
|
||||
)
|
||||
|
||||
assert list(identities) == [identity_arn]
|
||||
assert {
|
||||
call.kwargs["region"]
|
||||
for call in cloudtrail_client._lookup_events.call_args_list
|
||||
} == {AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1}
|
||||
|
||||
def test_same_role_sessions_aggregate_by_canonical_role_arn(self):
|
||||
role_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/platform/admin"
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.trails = {"trail": mock.MagicMock(is_multiregion=False)}
|
||||
|
||||
def lookup_events(trail, event_name, minutes):
|
||||
session_name = "session-one" if event_name == "ActionOne" else "session-two"
|
||||
return [
|
||||
{
|
||||
"CloudTrailEvent": json.dumps(
|
||||
{
|
||||
"userIdentity": {
|
||||
"type": "AssumedRole",
|
||||
"arn": f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:assumed-role/admin/{session_name}",
|
||||
"sessionContext": {"sessionIssuer": {"arn": role_arn}},
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
cloudtrail_client._lookup_events = lookup_events
|
||||
|
||||
identities = get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client, ["ActionOne", "ActionTwo"], 60
|
||||
)
|
||||
|
||||
assert list(identities) == [role_arn]
|
||||
resource, actions = identities[role_arn]
|
||||
assert resource.id == "role/platform/admin"
|
||||
assert actions == {"ActionOne", "ActionTwo"}
|
||||
|
||||
def test_different_roles_with_same_session_name_remain_distinct(self):
|
||||
first_role_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/first"
|
||||
second_role_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/second"
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.trails = {"trail": mock.MagicMock(is_multiregion=False)}
|
||||
cloudtrail_client._lookup_events = lambda trail, event_name, minutes: [
|
||||
{
|
||||
"CloudTrailEvent": json.dumps(
|
||||
{
|
||||
"userIdentity": {
|
||||
"type": "AssumedRole",
|
||||
"arn": f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:assumed-role/first/shared-session",
|
||||
"sessionContext": {
|
||||
"sessionIssuer": {"arn": first_role_arn}
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
"CloudTrailEvent": json.dumps(
|
||||
{
|
||||
"userIdentity": {
|
||||
"type": "AssumedRole",
|
||||
"arn": f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:assumed-role/second/shared-session",
|
||||
"sessionContext": {
|
||||
"sessionIssuer": {"arn": second_role_arn}
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
]
|
||||
|
||||
identities = get_cloudtrail_threat_detection_identities(
|
||||
cloudtrail_client, ["ActionOne"], 60
|
||||
)
|
||||
|
||||
assert set(identities) == {first_role_arn, second_role_arn}
|
||||
|
||||
+145
-14
@@ -1,7 +1,10 @@
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
from unittest import mock
|
||||
|
||||
from moto import mock_aws
|
||||
|
||||
from prowler.lib.outputs.finding import Finding
|
||||
from tests.providers.aws.utils import (
|
||||
AWS_ACCOUNT_NUMBER,
|
||||
AWS_REGION_US_EAST_1,
|
||||
@@ -17,6 +20,7 @@ def mock_get_trail_arn_template(region=None, *_) -> str:
|
||||
|
||||
|
||||
def mock__get_lookup_events__(trail=None, event_name=None, minutes=None, *_) -> list:
|
||||
del trail, minutes
|
||||
return [
|
||||
{
|
||||
"CloudTrailEvent": '{"eventName": "DescribeAccessEntry", "userIdentity": {"type": "IAMUser", "principalId": "EXAMPLE6E4XEGITWATV6R", "arn": "arn:aws:iam::123456789012:user/Attacker", "accountId": "123456789012", "accessKeyId": "AKIAIOSFODNN7EXAMPLE", "userName": "Attacker", "sessionContext": {"sessionIssuer": {}, "webIdFederationData": {}, "attributes": {"creationDate": "2023-07-19T21:11:57Z", "mfaAuthenticated": "false"}}}}'
|
||||
@@ -30,6 +34,7 @@ def mock__get_lookup_events__(trail=None, event_name=None, minutes=None, *_) ->
|
||||
def mock__get_lookup_events_aws_service__(
|
||||
trail=None, event_name=None, minutes=None, *_
|
||||
) -> list:
|
||||
del trail, minutes
|
||||
return [
|
||||
{
|
||||
"CloudTrailEvent": '{"eventName": "DescribeAccessEntry", "userIdentity": {"type": "AWSService", "principalId": "EXAMPLE6E4XEGITWATV6R", "accountId": "123456789012", "sessionContext": {"sessionIssuer": {}, "webIdFederationData": {}, "attributes": {"creationDate": "2023-07-19T21:11:57Z", "mfaAuthenticated": "false"}}}}'
|
||||
@@ -40,7 +45,69 @@ def mock__get_lookup_events_aws_service__(
|
||||
]
|
||||
|
||||
|
||||
def mock__get_lookup_events_assumed_role__(
|
||||
trail=None, event_name=None, minutes=None, *_
|
||||
) -> list:
|
||||
del trail, minutes
|
||||
session_name = (
|
||||
"enumeration-session-one"
|
||||
if event_name == "DescribeAccessEntry"
|
||||
else "enumeration-session-two"
|
||||
)
|
||||
return [
|
||||
{
|
||||
"CloudTrailEvent": json.dumps(
|
||||
{
|
||||
"userIdentity": {
|
||||
"type": "AssumedRole",
|
||||
"arn": f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:assumed-role/platform-attacker/{session_name}",
|
||||
"sessionContext": {
|
||||
"sessionIssuer": {
|
||||
"arn": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/security/platform-attacker"
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class Test_cloudtrail_threat_detection_enumeration:
|
||||
@mock_aws
|
||||
def test_unavailable_trail_inventory_is_manual(self):
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.trails = None
|
||||
cloudtrail_client.audit_config = {}
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_threat_detection_enumeration.cloudtrail_threat_detection_enumeration.cloudtrail_client",
|
||||
new=cloudtrail_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_threat_detection_enumeration.cloudtrail_threat_detection_enumeration import (
|
||||
cloudtrail_threat_detection_enumeration,
|
||||
)
|
||||
|
||||
result = cloudtrail_threat_detection_enumeration().execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "MANUAL"
|
||||
assert (
|
||||
"CloudTrail trails or events could not be retrieved"
|
||||
in result[0].status_extended
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_no_trails(self):
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
@@ -48,6 +115,9 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
cloudtrail_client._lookup_events = mock__get_lookup_events__
|
||||
cloudtrail_client._get_trail_arn_template = mock_get_trail_arn_template
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
|
||||
with (
|
||||
@@ -75,10 +145,8 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
assert result[0].resource["identity_type"] == "AWSAccount"
|
||||
|
||||
@mock_aws
|
||||
def test_no_potential_enumeration(self):
|
||||
@@ -92,6 +160,9 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_enumeration_actions": ENUMERATION_ACTIONS,
|
||||
@@ -127,10 +198,7 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
@mock_aws
|
||||
def test_potential_enumeration(self):
|
||||
@@ -144,6 +212,9 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_enumeration_actions": ENUMERATION_ACTIONS,
|
||||
@@ -178,12 +249,15 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
result[0].status_extended
|
||||
== "Potential enumeration attack detected from AWS IAMUser Attacker with a threshold of 1.0."
|
||||
)
|
||||
assert result[0].resource_id == "Attacker"
|
||||
assert result[0].resource_id == "user/Attacker"
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/Attacker"
|
||||
)
|
||||
assert result[0].resource["name"] == "Attacker"
|
||||
assert result[0].resource["identity_type"] == "IAMUser"
|
||||
assert result[0].check_metadata.ResourceType == "Other"
|
||||
|
||||
@mock_aws
|
||||
def test_big_threshold(self):
|
||||
@@ -197,6 +271,9 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_enumeration_actions": ENUMERATION_ACTIONS,
|
||||
@@ -232,10 +309,7 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
@mock_aws
|
||||
def test_potential_enumeration_from_aws_service(self):
|
||||
@@ -249,6 +323,9 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_enumeration_actions": ENUMERATION_ACTIONS,
|
||||
@@ -284,7 +361,61 @@ class Test_cloudtrail_threat_detection_enumeration:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
@mock_aws
|
||||
def test_assumed_role_sessions_aggregate_into_one_finding(self):
|
||||
aws_provider = set_mocked_aws_provider()
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.trails = {"us-east-1": mock.MagicMock()}
|
||||
cloudtrail_client.trails["us-east-1"].is_multiregion = False
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_enumeration_actions": [
|
||||
"DescribeAccessEntry",
|
||||
"DescribeAccountAttributes",
|
||||
],
|
||||
"threat_detection_enumeration_threshold": 0.6,
|
||||
"threat_detection_enumeration_minutes": 1440,
|
||||
}
|
||||
cloudtrail_client._lookup_events = mock__get_lookup_events_assumed_role__
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_threat_detection_enumeration.cloudtrail_threat_detection_enumeration.cloudtrail_client",
|
||||
new=cloudtrail_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_threat_detection_enumeration.cloudtrail_threat_detection_enumeration import (
|
||||
cloudtrail_threat_detection_enumeration,
|
||||
)
|
||||
|
||||
result = cloudtrail_threat_detection_enumeration().execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[0].resource_id == "role/security/platform-attacker"
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/security/platform-attacker"
|
||||
)
|
||||
assert result[0].resource["identity_type"] == "AssumedRole"
|
||||
assert result[0].resource["source_arn"].startswith("arn:aws:sts::")
|
||||
|
||||
finding = Finding.generate_output(
|
||||
aws_provider,
|
||||
result[0],
|
||||
SimpleNamespace(unix_timestamp=False, bulk_checks_metadata={}),
|
||||
)
|
||||
assert finding.resource_name == "role/security/platform-attacker"
|
||||
assert finding.resource_uid == result[0].resource_arn
|
||||
assert finding.resource_metadata == result[0].resource
|
||||
assert finding.uid.endswith("-role/security/platform-attacker")
|
||||
|
||||
+59
-17
@@ -17,6 +17,7 @@ def mock_get_trail_arn_template(region=None, *_) -> str:
|
||||
|
||||
|
||||
def mock__get_lookup_events__(trail=None, event_name=None, minutes=None, *_) -> list:
|
||||
del trail, minutes
|
||||
return [
|
||||
{
|
||||
"CloudTrailEvent": '{"eventName": "InvokeModel", "userIdentity": {"type": "IAMUser", "principalId": "EXAMPLE6E4XEGITWATV6R", "arn": "arn:aws:iam::123456789012:user/Attacker", "accountId": "123456789012", "accessKeyId": "AKIAIOSFODNN7EXAMPLE", "userName": "Attacker", "sessionContext": {"sessionIssuer": {}, "webIdFederationData": {}, "attributes": {"creationDate": "2023-07-19T21:11:57Z", "mfaAuthenticated": "false"}}}}'
|
||||
@@ -30,6 +31,7 @@ def mock__get_lookup_events__(trail=None, event_name=None, minutes=None, *_) ->
|
||||
def mock__get_lookup_events_aws_service__(
|
||||
trail=None, event_name=None, minutes=None, *_
|
||||
) -> list:
|
||||
del trail, minutes
|
||||
return [
|
||||
{
|
||||
"CloudTrailEvent": '{"eventName": "InvokeModel", "userIdentity": {"type": "AWSService", "principalId": "EXAMPLE6E4XEGITWATV6R", "accountId": "123456789012", "accessKeyId": "AKIAIOSFODNN7EXAMPLE", "sessionContext": {"sessionIssuer": {}, "webIdFederationData": {}, "attributes": {"creationDate": "2023-07-19T21:11:57Z", "mfaAuthenticated": "false"}}}}'
|
||||
@@ -41,6 +43,40 @@ def mock__get_lookup_events_aws_service__(
|
||||
|
||||
|
||||
class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
@mock_aws
|
||||
def test_unavailable_trail_inventory_is_manual(self):
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.trails = None
|
||||
cloudtrail_client.audit_config = {}
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_threat_detection_llm_jacking.cloudtrail_threat_detection_llm_jacking.cloudtrail_client",
|
||||
new=cloudtrail_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_threat_detection_llm_jacking.cloudtrail_threat_detection_llm_jacking import (
|
||||
cloudtrail_threat_detection_llm_jacking,
|
||||
)
|
||||
|
||||
result = cloudtrail_threat_detection_llm_jacking().execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "MANUAL"
|
||||
assert (
|
||||
"CloudTrail trails or events could not be retrieved"
|
||||
in result[0].status_extended
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_no_trails(self):
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
@@ -48,6 +84,9 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
cloudtrail_client._lookup_events = mock__get_lookup_events__
|
||||
cloudtrail_client._get_trail_arn_template = mock_get_trail_arn_template
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
|
||||
with (
|
||||
@@ -75,10 +114,8 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
assert result[0].resource["identity_type"] == "AWSAccount"
|
||||
|
||||
@mock_aws
|
||||
def test_no_potential_llm_jacking(self):
|
||||
@@ -89,6 +126,9 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_llm_jacking_actions": [],
|
||||
@@ -124,10 +164,7 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
@mock_aws
|
||||
def test_potential_priviledge_escalation(self):
|
||||
@@ -138,6 +175,9 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_llm_jacking_actions": [
|
||||
@@ -175,12 +215,14 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
result[0].status_extended
|
||||
== "Potential LLM Jacking attack detected from AWS IAMUser Attacker with a threshold of 1.0."
|
||||
)
|
||||
assert result[0].resource_id == "Attacker"
|
||||
assert result[0].resource_id == "user/Attacker"
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/Attacker"
|
||||
)
|
||||
assert result[0].resource["identity_type"] == "IAMUser"
|
||||
assert result[0].check_metadata.ResourceType == "Other"
|
||||
|
||||
@mock_aws
|
||||
def test_bigger_threshold(self):
|
||||
@@ -191,6 +233,9 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_llm_jacking_actions": [
|
||||
@@ -229,10 +274,7 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
@mock_aws
|
||||
def test_potential_enumeration_from_aws_service(self):
|
||||
@@ -243,6 +285,9 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_llm_jacking_actions": [
|
||||
@@ -281,7 +326,4 @@ class Test_cloudtrail_threat_detection_llm_jacking:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
+59
-17
@@ -17,6 +17,7 @@ def mock_get_trail_arn_template(region=None, *_) -> str:
|
||||
|
||||
|
||||
def mock__get_lookup_events__(trail=None, event_name=None, minutes=None, *_) -> list:
|
||||
del trail, minutes
|
||||
return [
|
||||
{
|
||||
"CloudTrailEvent": '{"eventName": "CreateLoginProfile", "userIdentity": {"type": "IAMUser", "principalId": "EXAMPLE6E4XEGITWATV6R", "arn": "arn:aws:iam::123456789012:user/Attacker", "accountId": "123456789012", "accessKeyId": "AKIAIOSFODNN7EXAMPLE", "userName": "Attacker", "sessionContext": {"sessionIssuer": {}, "webIdFederationData": {}, "attributes": {"creationDate": "2023-07-19T21:11:57Z", "mfaAuthenticated": "false"}}}}'
|
||||
@@ -30,6 +31,7 @@ def mock__get_lookup_events__(trail=None, event_name=None, minutes=None, *_) ->
|
||||
def mock__get_lookup_events_aws_service__(
|
||||
trail=None, event_name=None, minutes=None, *_
|
||||
) -> list:
|
||||
del trail, minutes
|
||||
return [
|
||||
{
|
||||
"CloudTrailEvent": '{"eventName": "CreateLoginProfile", "userIdentity": {"type": "AWSService", "principalId": "EXAMPLE6E4XEGITWATV6R", "accountId": "123456789012", "accessKeyId": "AKIAIOSFODNN7EXAMPLE", "sessionContext": {"sessionIssuer": {}, "webIdFederationData": {}, "attributes": {"creationDate": "2023-07-19T21:11:57Z", "mfaAuthenticated": "false"}}}}'
|
||||
@@ -41,6 +43,40 @@ def mock__get_lookup_events_aws_service__(
|
||||
|
||||
|
||||
class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
@mock_aws
|
||||
def test_unavailable_trail_inventory_is_manual(self):
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
cloudtrail_client.trails = None
|
||||
cloudtrail_client.audit_config = {}
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_threat_detection_privilege_escalation.cloudtrail_threat_detection_privilege_escalation.cloudtrail_client",
|
||||
new=cloudtrail_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_threat_detection_privilege_escalation.cloudtrail_threat_detection_privilege_escalation import (
|
||||
cloudtrail_threat_detection_privilege_escalation,
|
||||
)
|
||||
|
||||
result = cloudtrail_threat_detection_privilege_escalation().execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "MANUAL"
|
||||
assert (
|
||||
"CloudTrail trails or events could not be retrieved"
|
||||
in result[0].status_extended
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_no_trails(self):
|
||||
cloudtrail_client = mock.MagicMock()
|
||||
@@ -48,6 +84,9 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
cloudtrail_client._lookup_events = mock__get_lookup_events__
|
||||
cloudtrail_client._get_trail_arn_template = mock_get_trail_arn_template
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
|
||||
with (
|
||||
@@ -76,10 +115,8 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
assert result[0].resource["identity_type"] == "AWSAccount"
|
||||
|
||||
@mock_aws
|
||||
def test_no_potential_priviledge_escalation(self):
|
||||
@@ -90,6 +127,9 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_privilege_escalation_actions": [],
|
||||
@@ -126,10 +166,7 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
@mock_aws
|
||||
def test_potential_priviledge_escalation(self):
|
||||
@@ -140,6 +177,9 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_privilege_escalation_actions": [
|
||||
@@ -177,12 +217,14 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
result[0].status_extended
|
||||
== "Potential privilege escalation attack detected from AWS IAMUser Attacker with a threshold of 1.0."
|
||||
)
|
||||
assert result[0].resource_id == "Attacker"
|
||||
assert result[0].resource_id == "user/Attacker"
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/Attacker"
|
||||
)
|
||||
assert result[0].resource["identity_type"] == "IAMUser"
|
||||
assert result[0].check_metadata.ResourceType == "Other"
|
||||
|
||||
@mock_aws
|
||||
def test_bigger_threshold(self):
|
||||
@@ -193,6 +235,9 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_privilege_escalation_actions": [
|
||||
@@ -232,10 +277,7 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
@mock_aws
|
||||
def test_potential_enumeration_from_aws_service(self):
|
||||
@@ -246,6 +288,9 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
cloudtrail_client.trails["us-east-1"].s3_bucket_name = "bucket_test_us"
|
||||
cloudtrail_client.trails["us-east-1"].region = "us-east-1"
|
||||
cloudtrail_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
cloudtrail_client.audited_account_arn = (
|
||||
f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
)
|
||||
cloudtrail_client.region = AWS_REGION_US_EAST_1
|
||||
cloudtrail_client.audit_config = {
|
||||
"threat_detection_privilege_escalation_actions": [
|
||||
@@ -285,7 +330,4 @@ class Test_cloudtrail_threat_detection_privilege_escalation:
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:cloudtrail:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:trail"
|
||||
)
|
||||
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
|
||||
|
||||
Reference in New Issue
Block a user