feat(MITRE): add MITRE ATT&CK framework for AWS (#2537)

This commit is contained in:
Sergio Garcia
2023-06-30 12:24:05 +02:00
committed by GitHub
parent a58f4b2498
commit 4f033cec8d
7 changed files with 2318 additions and 63 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -2,10 +2,7 @@ import sys
from pydantic import parse_obj_as
from prowler.lib.check.compliance_models import (
Compliance_Base_Model,
Compliance_Requirement,
)
from prowler.lib.check.compliance_models import Compliance_Base_Model
from prowler.lib.check.models import Check_Metadata_Model
from prowler.lib.logger import logger
@@ -22,16 +19,7 @@ def update_checks_metadata_with_compliance(
compliance_requirements = []
# Verify if check is in the requirement
if check in requirement.Checks:
# Create the Compliance_Requirement
requirement = Compliance_Requirement(
Id=requirement.Id,
Description=requirement.Description,
Attributes=requirement.Attributes,
Checks=requirement.Checks,
)
# For the check metadata we don't need the "Checks" key
delattr(requirement, "Checks")
# Include the requirment into the check's framework requirements
# Include the requirement into the check's framework requirements
compliance_requirements.append(requirement)
# Create the Compliance_Model
compliance = Compliance_Base_Model(

View File

@@ -8,8 +8,8 @@ from prowler.lib.logger import logger
# ENS - Esquema Nacional de Seguridad - España
class ENS_Requirements_Nivel(str, Enum):
"""ENS V3 Requirements Level"""
class ENS_Requirement_Attribute_Nivel(str, Enum):
"""ENS V3 Requirement Attribute Level"""
opcional = "opcional"
bajo = "bajo"
@@ -17,8 +17,8 @@ class ENS_Requirements_Nivel(str, Enum):
alto = "alto"
class ENS_Requirements_Dimensiones(str, Enum):
"""ENS V3 Requirements Dimensions"""
class ENS_Requirement_Attribute_Dimensiones(str, Enum):
"""ENS V3 Requirement Attribute Dimensions"""
confidencialidad = "confidencialidad"
integridad = "integridad"
@@ -27,8 +27,8 @@ class ENS_Requirements_Dimensiones(str, Enum):
disponibilidad = "disponibilidad"
class ENS_Requirements_Tipos(str, Enum):
"""ENS Requirements Tipos"""
class ENS_Requirement_Attribute_Tipos(str, Enum):
"""ENS Requirement Attribute Tipos"""
refuerzo = "refuerzo"
requisito = "requisito"
@@ -36,21 +36,21 @@ class ENS_Requirements_Tipos(str, Enum):
medida = "medida"
class ENS_Requirements(BaseModel):
"""ENS V3 Framework Requirements"""
class ENS_Requirement_Attribute(BaseModel):
"""ENS V3 Framework Requirement Attribute"""
IdGrupoControl: str
Marco: str
Categoria: str
DescripcionControl: str
Tipo: ENS_Requirements_Tipos
Nivel: ENS_Requirements_Nivel
Dimensiones: list[ENS_Requirements_Dimensiones]
Tipo: ENS_Requirement_Attribute_Tipos
Nivel: ENS_Requirement_Attribute_Nivel
Dimensiones: list[ENS_Requirement_Attribute_Dimensiones]
# Generic Compliance Requirements
class Generic_Compliance_Requirements(BaseModel):
"""Generic Compliance Requirements"""
# Generic Compliance Requirement Attribute
class Generic_Compliance_Requirement_Attribute(BaseModel):
"""Generic Compliance Requirement Attribute"""
ItemId: str
Section: Optional[str]
@@ -60,27 +60,27 @@ class Generic_Compliance_Requirements(BaseModel):
Soc_Type: Optional[str]
class CIS_Requirements_Profile(str):
"""CIS Requirements Profile"""
class CIS_Requirement_Attribute_Profile(str):
"""CIS Requirement Attribute Profile"""
Level_1 = "Level 1"
Level_2 = "Level 2"
class CIS_Requirements_AssessmentStatus(str):
"""CIS Requirements Assessment Status"""
class CIS_Requirement_Attribute_AssessmentStatus(str):
"""CIS Requirement Attribute Assessment Status"""
Manual = "Manual"
Automated = "Automated"
# CIS Requirements
class CIS_Requirements(BaseModel):
"""CIS Requirements"""
# CIS Requirement Attribute
class CIS_Requirement_Attribute(BaseModel):
"""CIS Requirement Attribute"""
Section: str
Profile: CIS_Requirements_Profile
AssessmentStatus: CIS_Requirements_AssessmentStatus
Profile: CIS_Requirement_Attribute_Profile
AssessmentStatus: CIS_Requirement_Attribute_AssessmentStatus
Description: str
RationaleStatement: str
ImpactStatement: str
@@ -90,9 +90,9 @@ class CIS_Requirements(BaseModel):
References: str
# Well Architected Requirements
class AWS_Well_Architected_Requirements(BaseModel):
"""AWS Well Architected Requirements"""
# Well Architected Requirement Attribute
class AWS_Well_Architected_Requirement_Attribute(BaseModel):
"""AWS Well Architected Requirement Attribute"""
Name: str
WellArchitectedQuestionId: str
@@ -105,9 +105,9 @@ class AWS_Well_Architected_Requirements(BaseModel):
ImplementationGuidanceUrl: str
# ISO27001 Requirements
class ISO27001_2013_Requirements(BaseModel):
"""ISO27001 Requirements"""
# ISO27001 Requirement Attribute
class ISO27001_2013_Requirement_Attribute(BaseModel):
"""ISO27001 Requirement Attribute"""
Category: str
Objetive_ID: str
@@ -115,6 +115,31 @@ class ISO27001_2013_Requirements(BaseModel):
Check_Summary: str
# MITRE Requirement Attribute
class Mitre_Requirement_Attribute(BaseModel):
"""MITRE Requirement Attribute"""
AWSService: str
Category: str
Value: str
Comment: str
# MITRE Requirement
class Mitre_Requirement(BaseModel):
"""Mitre_Requirement holds the model for every MITRE requirement"""
Name: str
Id: str
Tactics: list[str]
SubTechniques: list[str]
Description: str
Platforms: list[str]
TechniqueURL: str
Attributes: list[Mitre_Requirement_Attribute]
Checks: list[str]
# Base Compliance Model
class Compliance_Requirement(BaseModel):
"""Compliance_Requirement holds the base model for every requirement within a compliance framework"""
@@ -124,11 +149,11 @@ class Compliance_Requirement(BaseModel):
Name: Optional[str]
Attributes: list[
Union[
CIS_Requirements,
ENS_Requirements,
Generic_Compliance_Requirements,
ISO27001_2013_Requirements,
AWS_Well_Architected_Requirements,
CIS_Requirement_Attribute,
ENS_Requirement_Attribute,
Generic_Compliance_Requirement_Attribute,
ISO27001_2013_Requirement_Attribute,
AWS_Well_Architected_Requirement_Attribute,
]
]
Checks: list[str]
@@ -141,7 +166,7 @@ class Compliance_Base_Model(BaseModel):
Provider: str
Version: Optional[str]
Description: str
Requirements: list[Compliance_Requirement]
Requirements: list[Union[Mitre_Requirement, Compliance_Requirement]]
@root_validator(pre=True)
# noqa: F841 - since vulture raises unused variable 'cls'

View File

@@ -13,7 +13,9 @@ from prowler.lib.outputs.models import (
Check_Output_CSV_CIS,
Check_Output_CSV_ENS_RD2022,
Check_Output_CSV_Generic_Compliance,
Check_Output_MITRE_ATTACK,
generate_csv_fields,
unroll_list,
)
@@ -84,11 +86,9 @@ def fill_compliance(output_options, finding, audit_info, file_descriptors):
elif compliance.Framework == "CIS" and "cis_" in str(
output_options.output_modes
):
compliance_output = "cis_" + compliance.Version + "_aws"
# Only with the version of CIS that was selected
if "cis_" + compliance.Version + "_aws" in str(
output_options.output_modes
):
compliance_output = "cis_" + compliance.Version + "_aws"
if compliance_output in str(output_options.output_modes):
for requirement in compliance.Requirements:
requirement_description = requirement.Description
requirement_id = requirement.Id
@@ -158,7 +158,9 @@ def fill_compliance(output_options, finding, audit_info, file_descriptors):
CheckId=finding.check_metadata.CheckID,
)
csv_header = generate_csv_fields(Check_Output_CSV_AWS_Well_Architected)
csv_header = generate_csv_fields(
Check_Output_CSV_AWS_Well_Architected
)
elif (
compliance.Framework == "ISO27001"
@@ -176,7 +178,7 @@ def fill_compliance(output_options, finding, audit_info, file_descriptors):
for requirement in compliance.Requirements:
requirement_description = requirement.Description
requirement_id = requirement.Id
requirement.Name
requirement_name = requirement.Name
for attribute in requirement.Attributes:
compliance_row = Check_Output_CSV_AWS_ISO27001_2013(
Provider=finding.check_metadata.Provider,
@@ -185,6 +187,7 @@ def fill_compliance(output_options, finding, audit_info, file_descriptors):
Region=finding.region,
AssessmentDate=timestamp.isoformat(),
Requirements_Id=requirement_id,
Requirements_Name=requirement_name,
Requirements_Description=requirement_description,
Requirements_Attributes_Category=attribute.Category,
Requirements_Attributes_Objetive_ID=attribute.Objetive_ID,
@@ -196,7 +199,60 @@ def fill_compliance(output_options, finding, audit_info, file_descriptors):
CheckId=finding.check_metadata.CheckID,
)
csv_header = generate_csv_fields(Check_Output_CSV_AWS_ISO27001_2013)
csv_header = generate_csv_fields(Check_Output_CSV_AWS_ISO27001_2013)
elif (
compliance.Framework == "MITRE-ATTACK"
and compliance.Version == ""
and compliance.Provider == "AWS"
):
compliance_output = compliance.Framework
if compliance.Version != "":
compliance_output += "_" + compliance.Version
if compliance.Provider != "":
compliance_output += "_" + compliance.Provider
compliance_output = compliance_output.lower().replace("-", "_")
if compliance_output in output_options.output_modes:
for requirement in compliance.Requirements:
requirement_description = requirement.Description
requirement_id = requirement.Id
requirement_name = requirement.Name
attributes_aws_services = ""
attributes_categories = ""
attributes_values = ""
attributes_comments = ""
for attribute in requirement.Attributes:
attributes_aws_services += attribute.AWSService + "\n"
attributes_categories += attribute.Category + "\n"
attributes_values += attribute.Value + "\n"
attributes_comments += attribute.Comment + "\n"
compliance_row = Check_Output_MITRE_ATTACK(
Provider=finding.check_metadata.Provider,
Description=compliance.Description,
AccountId=audit_info.audited_account,
Region=finding.region,
AssessmentDate=timestamp.isoformat(),
Requirements_Id=requirement_id,
Requirements_Description=requirement_description,
Requirements_Name=requirement_name,
Requirements_Tactics=unroll_list(requirement.Tactics),
Requirements_SubTechniques=unroll_list(
requirement.SubTechniques
),
Requirements_Platforms=unroll_list(requirement.Platforms),
Requirements_TechniqueURL=requirement.TechniqueURL,
Requirements_Attributes_AWSServices=attributes_aws_services,
Requirements_Attributes_Categories=attributes_categories,
Requirements_Attributes_Values=attributes_values,
Requirements_Attributes_Comments=attributes_comments,
Status=finding.status,
StatusExtended=finding.status_extended,
ResourceId=finding.resource_id,
CheckId=finding.check_metadata.CheckID,
)
csv_header = generate_csv_fields(Check_Output_MITRE_ATTACK)
else:
compliance_output = compliance.Framework
@@ -230,7 +286,9 @@ def fill_compliance(output_options, finding, audit_info, file_descriptors):
CheckId=finding.check_metadata.CheckID,
)
csv_header = generate_csv_fields(Check_Output_CSV_Generic_Compliance)
csv_header = generate_csv_fields(
Check_Output_CSV_Generic_Compliance
)
if compliance_row:
csv_writer = DictWriter(
@@ -309,7 +367,7 @@ def display_compliance_table(
# Add results to table
for marco in sorted(marcos):
ens_compliance_table["Proveedor"].append("aws")
ens_compliance_table["Proveedor"].append(compliance.Provider)
ens_compliance_table["Marco/Categoria"].append(marco)
ens_compliance_table["Estado"].append(marcos[marco]["Estado"])
ens_compliance_table["Opcional"].append(
@@ -401,7 +459,7 @@ def display_compliance_table(
# Add results to table
sections = dict(sorted(sections.items()))
for section in sections:
cis_compliance_table["Provider"].append("aws")
cis_compliance_table["Provider"].append(compliance.Provider)
cis_compliance_table["Section"].append(section)
if sections[section]["Level 1"]["FAIL"] > 0:
cis_compliance_table["Level 1"].append(
@@ -449,6 +507,77 @@ def display_compliance_table(
print(
f" - CSV: {output_directory}/{output_filename}_{compliance_framework}.csv\n"
)
elif "mitre_attack" in compliance_framework:
tactics = {}
mitre_compliance_table = {
"Provider": [],
"Tactic": [],
"Status": [],
}
pass_count = fail_count = 0
for finding in findings:
check = bulk_checks_metadata[finding.check_metadata.CheckID]
check_compliances = check.Compliance
for compliance in check_compliances:
if (
"MITRE-ATTACK" in compliance.Framework
and compliance.Version in compliance_framework
):
compliance_fm = compliance.Framework
for requirement in compliance.Requirements:
for tactic in requirement.Tactics:
if tactic not in tactics:
tactics[tactic] = {"FAIL": 0, "PASS": 0}
if finding.status == "FAIL":
fail_count += 1
tactics[tactic]["FAIL"] += 1
elif finding.status == "PASS":
pass_count += 1
tactics[tactic]["PASS"] += 1
# Add results to table
tactics = dict(sorted(tactics.items()))
for tactic in tactics:
mitre_compliance_table["Provider"].append(compliance.Provider)
mitre_compliance_table["Tactic"].append(tactic)
if tactics[tactic]["FAIL"] > 0:
mitre_compliance_table["Status"].append(
f"{Fore.RED}FAIL({tactics[tactic]['FAIL']}){Style.RESET_ALL}"
)
else:
mitre_compliance_table["Status"].append(
f"{Fore.GREEN}PASS({tactics[tactic]['PASS']}){Style.RESET_ALL}"
)
if fail_count + pass_count < 1:
print(
f"\n {Style.BRIGHT}There are no resources for {Fore.YELLOW}{compliance_fm}{Style.RESET_ALL}.\n"
)
else:
print(
f"\nCompliance Status of {Fore.YELLOW}{compliance_fm}{Style.RESET_ALL} Framework:"
)
overview_table = [
[
f"{Fore.RED}{round(fail_count/(fail_count+pass_count)*100, 2)}% ({fail_count}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(pass_count/(fail_count+pass_count)*100, 2)}% ({pass_count}) PASS{Style.RESET_ALL}",
]
]
print(tabulate(overview_table, tablefmt="rounded_grid"))
print(
f"\nFramework {Fore.YELLOW}{compliance_fm}{Style.RESET_ALL} Results:"
)
print(
tabulate(
mitre_compliance_table, headers="keys", tablefmt="rounded_grid"
)
)
print(
f"{Style.BRIGHT}* Only sections containing results appear.{Style.RESET_ALL}"
)
print(f"\nDetailed results of {compliance_fm} are in:")
print(
f" - CSV: {output_directory}/{output_filename}_{compliance_framework}.csv\n"
)
else:
print(f"\nDetailed results of {compliance_framework.upper()} are in:")
print(

View File

@@ -19,6 +19,7 @@ from prowler.lib.outputs.models import (
Check_Output_CSV_CIS,
Check_Output_CSV_ENS_RD2022,
Check_Output_CSV_Generic_Compliance,
Check_Output_MITRE_ATTACK,
Gcp_Check_Output_CSV,
generate_csv_fields,
)
@@ -187,6 +188,16 @@ def fill_file_descriptors(output_modes, output_directory, output_filename, audit
)
file_descriptors.update({output_mode: file_descriptor})
elif output_mode == "mitre_attack_aws":
filename = f"{output_directory}/{output_filename}_mitre_attack_aws{csv_file_suffix}"
file_descriptor = initialize_file_descriptor(
filename,
output_mode,
audit_info,
Check_Output_MITRE_ATTACK,
)
file_descriptors.update({output_mode: file_descriptor})
else:
# Generic Compliance framework
filename = f"{output_directory}/{output_filename}_{output_mode}{csv_file_suffix}"

View File

@@ -265,7 +265,7 @@ def parse_json_tags(tags: list):
def generate_csv_fields(format: Any) -> list[str]:
"""Generates the CSV headers for the given class"""
csv_fields = []
# __fields__ is alwayis available in the Pydantic's BaseModel class
# __fields__ is always available in the Pydantic's BaseModel class
for field in format.__dict__.get("__fields__").keys():
csv_fields.append(field)
return csv_fields
@@ -487,6 +487,33 @@ class Gcp_Check_Output_JSON(Check_Output_JSON):
super().__init__(**metadata)
class Check_Output_MITRE_ATTACK(BaseModel):
"""
Check_Output_MITRE_ATTACK generates a finding's output in CSV MITRE ATTACK format.
"""
Provider: str
Description: str
AccountId: str
Region: str
AssessmentDate: str
Requirements_Id: str
Requirements_Name: str
Requirements_Description: str
Requirements_Tactics: str
Requirements_SubTechniques: str
Requirements_Platforms: str
Requirements_TechniqueURL: str
Requirements_Attributes_AWSServices: str
Requirements_Attributes_Categories: str
Requirements_Attributes_Values: str
Requirements_Attributes_Comments: str
Status: str
StatusExtended: str
ResourceId: str
CheckId: str
class Check_Output_CSV_ENS_RD2022(BaseModel):
"""
Check_Output_CSV_ENS_RD2022 generates a finding's output in CSV ENS RD2022 format.

View File

@@ -20,7 +20,7 @@ from prowler.config.config import (
timestamp_utc,
)
from prowler.lib.check.compliance_models import (
CIS_Requirements,
CIS_Requirement_Attribute,
Compliance_Base_Model,
Compliance_Requirement,
)
@@ -1351,7 +1351,7 @@ class Test_Outputs:
Id="2.1.3",
Description="Ensure MFA Delete is enabled on S3 buckets",
Attributes=[
CIS_Requirements(
CIS_Requirement_Attribute(
Section="2.1. Simple Storage Service (S3)",
Profile="Level 1",
AssessmentStatus="Automated",
@@ -1378,7 +1378,7 @@ class Test_Outputs:
Id="2.1.3",
Description="Ensure MFA Delete is enabled on S3 buckets",
Attributes=[
CIS_Requirements(
CIS_Requirement_Attribute(
Section="2.1. Simple Storage Service (S3)",
Profile="Level 1",
AssessmentStatus="Automated",