示例#1
0
 def account_id(self):  # pragma: no cover
     """Return the account ID"""
     if "arn:aws:iam::aws:" in self.arn:
         return "N/A"
     else:
         account_id = get_account_from_arn(self.arn)
         return account_id
示例#2
0
 def set_rbp(self, evil_policy: dict) -> ResponseMessage:
     logger.debug("Setting resource policy for %s" % self.arn)
     new_policy_document = PolicyDocument(
         policy=evil_policy,
         service=self.service,
         override_action=self.override_action,
         include_resource_block=self.include_resource_block,
         override_resource_block=self.override_resource_block,
         override_account_id_instead_of_principal=self.
         override_account_id_instead_of_principal,
     )
     new_policy_json = {"Version": "2012-10-17", "Statement": []}
     current_sids = get_sid_names_with_error_handling(self.original_policy)
     success = True
     try:
         for statement in new_policy_document.statements:
             if statement.sid not in current_sids:
                 if ":" in statement.aws_principals[0]:
                     account_id = get_account_from_arn(
                         statement.aws_principals[0])
                     principal = f"arn:aws:iam::{account_id}:root"
                 elif "*" == statement.aws_principals[0]:
                     principal = "*"
                 else:
                     principal = statement.aws_principals[0]
                 try:
                     self.client.add_layer_version_permission(
                         LayerName=self.arn_without_version,
                         VersionNumber=self.version,
                         StatementId=statement.sid,
                         Action="lambda:GetLayerVersion",
                         Principal=principal,
                     )
                 except botocore.exceptions.ClientError as error:
                     success = False
                     logger.critical(
                         f"Operation was not successful for {self.service} {self.resource_type} "
                         f"{self.name}. %s" % error)
             new_policy_json["Statement"].append(
                 json.loads(statement.__str__()))
         message = "success"
     except botocore.exceptions.ClientError as error:
         message = str(error)
         success = False
     policy_document = self._get_rbp().policy_document
     response_message = ResponseMessage(
         message=message,
         operation="set_rbp",
         success=success,
         evil_principal="",
         victim_resource_arn=self.arn,
         original_policy=self.original_policy,
         updated_policy=policy_document.json,
         resource_type=self.resource_type,
         resource_name=self.name,
         service=self.service)
     return response_message
示例#3
0
 def parse_evil_principal(self, evil_principal: str) -> str:
     if ":" in evil_principal:
         evil_account_id = get_account_from_arn(evil_principal)
     # RDS requires publicly shared snapshots to be supplied via the value "all"
     elif evil_principal == "*":
         evil_account_id = "all"
     # Otherwise, the evil_principal is an account ID
     else:
         evil_account_id = evil_principal
     return evil_account_id
示例#4
0
 def account_id(self):
     """Return the account ID, if applicable."""
     if "arn:aws:iam::aws:" in self.arn:
         return "N/A"  # pragma: no cover
     else:
         try:
             account_id = get_account_from_arn(self.arn)
             return account_id
         except IndexError as i_e:
             logger.debug(i_e)
             return "N/A"
示例#5
0
def scan_account_authorization_details(
    account_authorization_details_cfg,
    exclusions,
    account_name="default",
    output_directory=os.getcwd(),
    write_data_files=False,
    minimize=False,
):  # pragma: no cover
    """
    Given the path to account authorization details files and the exclusions config file, scan all inline and
    managed policies in the account to identify actions that do not leverage resource constraints.
    """

    logger.debug("Identifying modify-only actions that are not leveraging "
                 "resource constraints...")
    check_authorization_details_schema(account_authorization_details_cfg)
    authorization_details = AuthorizationDetails(
        account_authorization_details_cfg, exclusions)
    results = authorization_details.results

    # Lazy method to get an account ID
    account_id = None
    for role in results.get("roles"):
        if "arn:aws:iam::aws:" not in results["roles"][role]["arn"]:
            account_id = get_account_from_arn(results["roles"][role]["arn"])
            break

    html_report = HTMLReport(
        account_id=account_id,
        account_name=account_name,
        results=results,
        minimize=minimize,
    )
    rendered_report = html_report.get_html_report()

    # Raw data file
    if write_data_files:
        if output_directory is None:
            output_directory = os.getcwd()

        results_data_file = os.path.join(output_directory,
                                         f"iam-results-{account_name}.json")
        results_data_filepath = write_results_data_file(
            authorization_details.results, results_data_file)
        print(f"Results data saved: {str(results_data_filepath)}")

        findings_data_file = os.path.join(output_directory,
                                          f"iam-findings-{account_name}.json")
        findings_data_filepath = write_results_data_file(
            results, findings_data_file)
        print(f"Findings data file saved: {str(findings_data_filepath)}")

    return rendered_report
示例#6
0
def build_arn_table(db_session, service):
    """
    Builds the ARN Table - the table of resource types - in the SQLite database.

    :param db_session: SQLAlchemy database session.
    :param service: The AWS service prefix
    """
    directory = os.path.abspath(os.path.dirname(__file__)) + "/data/docs/"
    html_list = get_html(directory, service)
    for df_list in html_list:
        for df in df_list:  # pylint: disable=invalid-name
            table = json.loads(df.to_json(orient="split"))
            table_data = df
            if "Resource Types" in table_data and "ARN" in table_data:
                for i in range(len(table["data"])):
                    # Replace the random spaces in the ARN
                    temp_raw_arn = table["data"][i][1].replace(" ", "")
                    # Handle resource ARN path
                    if get_resource_path_from_arn(temp_raw_arn):
                        resource_path = get_resource_path_from_arn(temp_raw_arn)
                    else:
                        resource_path = ""
                    # Handle condition keys
                    if table["data"][i][2] is None:
                        condition_keys = None
                    # If there are multiple condition keys, make them comma separated
                    # Otherwise, if we ingest them as-is, it will show up as
                    # two spaces
                    elif "  " in table["data"][i][2]:
                        condition_keys = get_comma_separated_condition_keys(
                            table["data"][i][2]
                        )
                    else:
                        condition_keys = table["data"][i][2]
                    db_session.add(
                        ArnTable(
                            resource_type_name=table["data"][i][0],
                            raw_arn=str(temp_raw_arn),
                            arn="arn",
                            partition=get_partition_from_arn(temp_raw_arn),
                            service=get_service_from_arn(temp_raw_arn),
                            region=get_region_from_arn(temp_raw_arn),
                            account=get_account_from_arn(temp_raw_arn),
                            resource=get_resource_from_arn(temp_raw_arn),
                            resource_path=resource_path,
                            condition_keys=condition_keys,
                        )
                    )
                    db_session.commit()
示例#7
0
 def set_rbp(self, evil_policy: dict) -> ResponseMessage:
     logger.debug("Setting resource policy for %s" % self.arn)
     new_policy_document = PolicyDocument(
         policy=evil_policy,
         service=self.service,
         override_action=self.override_action,
         include_resource_block=self.include_resource_block,
         override_resource_block=self.override_resource_block,
         override_account_id_instead_of_principal=self.
         override_account_id_instead_of_principal,
     )
     new_policy_json = {"Version": "2012-10-17", "Statement": []}
     current_sids = get_sid_names_with_error_handling(self.original_policy)
     try:
         for statement in new_policy_document.statements:
             if statement.sid not in current_sids:
                 account_ids = []
                 for principal in statement.aws_principals:
                     if ":" in principal:
                         account_ids.append(get_account_from_arn(principal))
                 self.client.add_permission(
                     QueueUrl=self.queue_url,
                     Label=statement.sid,
                     AWSAccountIds=account_ids,
                     Actions=self.sqs_actions_without_prefixes(
                         statement.actions))
             else:
                 new_policy_json["Statement"].append(
                     json.loads(statement.__str__()))
         message = "success"
         success = True
     except botocore.exceptions.ClientError as error:
         message = str(error)
         success = False
     get_rbp_response = self._get_rbp()
     response_message = ResponseMessage(
         message=message,
         operation="set_rbp",
         success=success,
         evil_principal="",
         victim_resource_arn=self.arn,
         original_policy=self.original_policy,
         updated_policy=get_rbp_response.policy_document.json,
         resource_type=self.resource_type,
         resource_name=self.name,
         service=self.service)
     return response_message
示例#8
0
async def get_resource_account(arn: str) -> str:
    """Return the AWS account ID that owns a resource.

    In most cases, this will pull the ID directly from the ARN.
    If we are unsuccessful in pulling the account from ARN, we try to grab it from our resources cache
    """
    red = await RedisHandler().redis()
    resource_account: str = get_account_from_arn(arn)
    if resource_account:
        return resource_account

    resources_from_aws_config_redis_key: str = config.get(
        "aws_config_cache.redis_key", "AWSCONFIG_RESOURCE_CACHE"
    )

    if not red.exists(resources_from_aws_config_redis_key):
        # This will force a refresh of our redis cache if the data exists in S3
        await retrieve_json_data_from_redis_or_s3(
            redis_key=resources_from_aws_config_redis_key,
            s3_bucket=config.get("aws_config_cache_combined.s3.bucket"),
            s3_key=config.get("aws_config_cache_combined.s3.file"),
            redis_data_type="hash",
        )
    resource_info = await redis_hget(resources_from_aws_config_redis_key, arn)
    if resource_info:
        return json.loads(resource_info).get("accountId", "")
    elif "arn:aws:s3:::" in arn:
        # Try to retrieve S3 bucket information from S3 cache. This is inefficient and we should ideally have
        # retrieved this info from our AWS Config cache, but we've encountered problems with AWS Config historically
        # that have necessitated this code.
        s3_cache = await retrieve_json_data_from_redis_or_s3(
            redis_key=config.get("redis.s3_buckets_key", "S3_BUCKETS"),
            redis_data_type="hash",
        )
        search_bucket_name = arn.split(":")[-1]
        for bucket_account_id, buckets in s3_cache.items():
            buckets_j = json.loads(buckets)
            if search_bucket_name in buckets_j:
                return bucket_account_id
    return ""
示例#9
0
    def add_myself(self,
                   evil_principal: str,
                   dry_run: bool = False) -> ResponseMessage:
        """Add your rogue principal to the AWS resource"""
        logger.debug(f"Adding {evil_principal} to {self.arn}")
        # Case: principal = "arn:aws:iam::999988887777:user/mwahahaha"
        if ":" in evil_principal:
            evil_principal_account = get_account_from_arn(evil_principal)
        # Case: Principal = * or Principal = 999988887777
        else:
            evil_principal_account = evil_principal
        evil_policy = {
            "Version":
            "2012-10-17",
            "Statement": [{
                "Sid":
                "1",
                "Effect":
                "Allow",
                "Principal": {
                    "AWS": evil_principal_account
                },
                "Action": [
                    "acm-pca:DescribeCertificateAuthority",
                    "acm-pca:GetCertificate",
                    "acm-pca:GetCertificateAuthorityCertificate",
                    "acm-pca:ListPermissions", "acm-pca:ListTags"
                ],
                "Resource":
                self.arn
            }, {
                "Sid": "1",
                "Effect": "Allow",
                "Principal": {
                    "AWS": evil_principal_account
                },
                "Action": ["acm-pca:IssueCertificate"],
                "Resource": self.arn,
                "Condition": {
                    "StringEquals": {
                        "acm-pca:TemplateArn":
                        "arn:aws:acm-pca:::template/EndEntityCertificate/V1"
                    }
                }
            }]
        }

        if dry_run:
            operation = "DRY_RUN_ADD_MYSELF"
            message = operation
            tmp = self._get_rbp()
            success = tmp.success
        else:
            operation = "ADD_MYSELF"
            self.undo(evil_principal=evil_principal)
            set_rbp_response = self.set_rbp(evil_policy=evil_policy)
            success = set_rbp_response.success
            message = set_rbp_response.message
        response_message = ResponseMessage(
            message=message,
            operation=operation,
            success=success,
            evil_principal=evil_principal,
            victim_resource_arn=self.arn,
            original_policy=self.original_policy,
            updated_policy=evil_policy,
            resource_type=self.resource_type,
            resource_name=self.name,
            service=self.service)
        return response_message
 def account_id(self) -> str:  # pragma: no cover
     """Return the account ID"""
     if is_aws_managed(self.arn):
         return "N/A"
     else:
         return get_account_from_arn(self.arn)  # type: ignore
 def account_id(self):
     """Return the account ID"""
     account_id = get_account_from_arn(self.arn)
     return account_id