diff --git a/checks/check_extra764 b/checks/check_extra764 index 8ef4a80654..a479531e87 100644 --- a/checks/check_extra764 +++ b/checks/check_extra764 @@ -34,7 +34,7 @@ extra764(){ fi # https://aws.amazon.com/premiumsupport/knowledge-center/s3-bucket-policy-for-config-rule/ - CHECK_BUCKET_STP_POLICY_PRESENT=$(cat $TEMP_STP_POLICY_FILE | jq --arg arn "arn:aws:s3:::${bucket}/*" '.Statement[]|select(((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and .Action=="s3:*" and (.Resource|type == "array") and (.Resource|map({(.):0})[]|has($arn)) and (.Resource|map({(.):0})[]|has($arn+"/*")) and .Condition.Bool."aws:SecureTransport" == "false")') + CHECK_BUCKET_STP_POLICY_PRESENT=$(cat $TEMP_STP_POLICY_FILE | jq --arg arn "arn:aws:s3:::${bucket}/*" '.Statement[]|select((((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")) and .Action=="s3:*" and (.Resource|type == "array") and (.Resource|map({(.):0})[]|has($arn)) and (.Resource|map({(.):0})[]|has($arn+"/*")) and .Condition.Bool."aws:SecureTransport" == "false")') if [[ $CHECK_BUCKET_STP_POLICY_PRESENT ]]; then textPass "Bucket $bucket has S3 bucket policy to deny requests over insecure transport" else diff --git a/checks/check_extra766 b/checks/check_extra766 new file mode 100644 index 0000000000..2382f4ace8 --- /dev/null +++ b/checks/check_extra766 @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy +# of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +# Remediation: +# +# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#configuring-instance-metadata-service +# +# aws ecr put-image-scanning-configuration \ +# --region \ +# --repository-name \ +# --image-scanning-configuration scanOnPush=true + + +CHECK_ID_extra766="7.66" +CHECK_TITLE_extra766="[extra766] Check if ECR image scan on push is enabled (Not Scored) (Not part of CIS benchmark)" +CHECK_SCORED_extra766="NOT_SCORED" +CHECK_TYPE_extra766="EXTRA" +CHECK_ALTERNATE_check766="extra766" + +extra766(){ + for regx in $REGIONS; do + LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[*].[repositoryName]" --output text 2>&1) + if [[ $LIST_ECR_REPOS ]]; then + for repo in $LIST_ECR_REPOS; do + SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[?repositoryName==\`$repo\`].[imageScanningConfiguration.scanOnPush]" --output text|grep True) + if [[ $SCAN_ENABLED ]];then + textPass "$regx: ECR repository $repo has scan on push enabled" "$regx" + else + textFail "$regx: ECR repository $repo has scan on push disabled!" "$regx" + fi + done + else + textInfo "$regx: No ECR repositories found" "$regx" + fi + done +} diff --git a/checks/check_extra768 b/checks/check_extra768 index bb57bb87c1..2c10e1dde1 100644 --- a/checks/check_extra768 +++ b/checks/check_extra768 @@ -52,4 +52,4 @@ extra768(){ fi done # rm -rf $SECRETS_TEMP_FOLDER -} \ No newline at end of file +} diff --git a/checks/check_extra769 b/checks/check_extra769 new file mode 100644 index 0000000000..8ec529e23a --- /dev/null +++ b/checks/check_extra769 @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy +# of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +CHECK_ID_extra769="7.69" +CHECK_TITLE_extra769="[extra769] Check if IAM Access Analyzer is enabled and its findings (Not Scored) (Not part of CIS benchmark)" +CHECK_SCORED_extra769="NOT_SCORED" +CHECK_TYPE_extra769="EXTRA" +CHECK_ALTERNATE_check769="extra769" + +extra769(){ + for regx in $REGIONS; do + LIST_OF_ACCESS_ANALYZERS=$($AWSCLI accessanalyzer list-analyzers $PROFILE_OPT --region $regx --query analyzers[*].arn --output text) + if [[ $LIST_OF_ACCESS_ANALYZERS ]]; then + for accessAnalyzerArn in $LIST_OF_ACCESS_ANALYZERS;do + ANALYZER_ACTIVE_FINDINGS_COUNT=$($AWSCLI accessanalyzer list-findings $PROFILE_OPT --region $regx --analyzer-arn $accessAnalyzerArn --query 'findings[?status == `ACTIVE`].[id,status]' --output text | wc -l | tr -d ' ') + if [[ $ANALYZER_ACTIVE_FINDINGS_COUNT -eq 0 ]];then + textPass "$regx: IAM Access Analyzer $accessAnalyzerArn has no active findings" "$regx" + else + textInfo "$regx: IAM Access Analyzer $accessAnalyzerArn has $ANALYZER_ACTIVE_FINDINGS_COUNT active findings" "$regx" + fi + done + else + textInfo "$regx: No IAM Access Analyzers found" "$regx" + fi + done +} diff --git a/groups/group7_extras b/groups/group7_extras index c3d80d3487..c9eaa5ad63 100644 --- a/groups/group7_extras +++ b/groups/group7_extras @@ -15,7 +15,7 @@ GROUP_ID[7]='extras' GROUP_NUMBER[7]='7.0' GROUP_TITLE[7]='Extras - [extras] **********************************************' GROUP_RUN_BY_DEFAULT[7]='Y' # run it when execute_all is called -GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra737,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra756,extra757,extra758,extra761,extra762,extra763,extra764,extra765,extra767' +GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra737,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra756,extra757,extra758,extra761,extra762,extra763,extra764,extra765,extra766,extra767,extra768,extra769' # Extras 759 and 760 (lambda variables and code secrets finder are not included) # to run detect-secrets use `./prowler -g secrets` diff --git a/iam/prowler-additions-policy.json b/iam/prowler-additions-policy.json new file mode 100644 index 0000000000..c706b6f487 --- /dev/null +++ b/iam/prowler-additions-policy.json @@ -0,0 +1,106 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "apigateway:get*", + "apigatewayv2:get*", + "aws-marketplace:viewsubscriptions", + "batch:listjobs", + "clouddirectory:listappliedschemaarns", + "clouddirectory:listdevelopmentschemaarns", + "clouddirectory:listpublishedschemaarns", + "cloudformation:list*", + "cloudhsm:listavailablezones", + "cloudsearch:list*", + "cloudwatch:get*", + "cloudwatch:list*", + "codebuild:listbuilds*", + "codestar:verify*", + "cognito-identity:listidentities", + "cognito-idp:list*", + "cognito-sync:listdatasets", + "connect:list*", + "datapipeline:getaccountlimits", + "dax:describeclusters", + "dax:describedefaultparameters", + "dax:describeevents", + "dax:describeparametergroups", + "dax:describeparameters", + "dax:describesubnetgroups", + "dax:describetable", + "dax:listtables", + "devicefarm:list*", + "discovery:list*", + "dms:list*", + "dynamodb:describebackup", + "dynamodb:describeglobaltablesettings", + "dynamodb:describelimits", + "dynamodb:describereservedcapacity", + "dynamodb:describereservedcapacityofferings", + "dynamodb:describestream", + "dynamodb:listtagsofresource", + "ec2:get*", + "ecr:describe*", + "ecr:listimages", + "elasticbeanstalk:listavailablesolutionstacks", + "elasticmapreduce:list*", + "elastictranscoder:list*", + "gamelift:list*", + "glacier:list*", + "importexport:listjobs", + "lex:getbotaliases", + "lex:getbotchannelassociations", + "lex:getbots", + "lex:getbotversions", + "lex:getintents", + "lex:getintentversions", + "lex:getslottypes", + "lex:getslottypeversions", + "lex:getutterancesview", + "lightsail:getblueprints", + "lightsail:getbundles", + "lightsail:getinstancesnapshots", + "lightsail:getkeypair", + "lightsail:getregions", + "lightsail:getstaticips", + "lightsail:isvpcpeered", + "machinelearning:describe*", + "mobilehub:listavailablefeatures", + "mobilehub:listavailableregions", + "mobilehub:listprojects", + "mobiletargeting:getapplicationsettings", + "mobiletargeting:getcampaigns", + "mobiletargeting:getimportjobs", + "mobiletargeting:getsegments", + "opsworks-cm:describe*", + "opsworks:describe*", + "polly:describe*", + "polly:list*", + "redshift:viewqueriesinconsole", + "route53domains:list*", + "s3:listbucket", + "sdb:list*", + "secretsmanager:listsecretversionids", + "servicecatalog:list*", + "ses:list*", + "ses:sendemail", + "sns:list*", + "sqs:listqueuetags", + "ssm:listassociations", + "states:listactivities", + "support:describe*", + "swf:list*", + "tag:gettagkeys", + "trustedadvisor:describe*", + "waf-regional:list*", + "waf:list*", + "workdocs:describeavailabledirectories", + "workdocs:describeinstances", + "workmail:describe*" + ], + "Effect": "Allow", + "Resource": "*" + } + ] +} \ No newline at end of file diff --git a/include/check3x b/include/check3x index 70bf9b910a..5b75d3151a 100644 --- a/include/check3x +++ b/include/check3x @@ -14,30 +14,36 @@ check3x(){ local CHECK_WARN local CHECK_CROSS_ACCOUNT_WARN - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text| tr '\011' '\012' | awk -F: '{print $7}') + DESCRIBE_TRAILS_CACHE=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION") + TRAIL_LIST=$(echo $DESCRIBE_TRAILS_CACHE | jq -r '.trailList[]|@base64') CURRENT_ACCOUNT_ID=$($AWSCLI sts $PROFILE_OPT get-caller-identity --region "$REGION" --query Account --output text) + CLOUDWATCH_LOGGROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text| tr '\011' '\012' | awk -F: '{print $7}') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | grep "$group" | awk -F: '{ print $4 }' | head -n 1) - CLOUDWATCH_ACCOUNT_ID=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | grep "$group" | awk -F: '{ print $5 }' | head -n 1) - if [ "$CLOUDWATCH_ACCOUNT_ID" == "$CURRENT_ACCOUNT_ID" ];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name "$group" $PROFILE_OPT --region "$CLOUDWATCH_LOGGROUP_REGION" --output text | grep METRICFILTERS | grep -E "$grep_filter" | awk '{ print $3 }') + if [[ $CLOUDWATCH_LOGGROUP != "" ]]; then + for group_obj_enc in $TRAIL_LIST; do + group_obj_raw=$(echo $group_obj_enc | decode_report) + CLOUDWATCH_LOGGROUP_NAME=$(echo $group_obj_raw | jq -r '.CloudWatchLogsLogGroupArn|split(":")[6]') + CLOUDWATCH_LOGGROUP_REGION=$(echo $group_obj_raw | jq -r '.CloudWatchLogsLogGroupArn|split(":")[3]') + CLOUDWATCH_LOGGROUP_ACCOUNT=$(echo $group_obj_raw | jq -r '.CloudWatchLogsLogGroupArn|split(":")[4]') + if [ "$CLOUDWATCH_LOGGROUP_ACCOUNT" == "$CURRENT_ACCOUNT_ID" ];then + # Filter control and whitespace from .metricFilters[*].filterPattern for easier matching later + METRICFILTER_CACHE=$($AWSCLI logs describe-metric-filters --log-group-name "$CLOUDWATCH_LOGGROUP_NAME" $PROFILE_OPT --region "$CLOUDWATCH_LOGGROUP_REGION"|jq '.metricFilters|=map(.filterPattern|=gsub("[[:space:]]+"; " "))') + METRICFILTER_SET=$(echo $METRICFILTER_CACHE | jq -r --arg re "$grep_filter" '.metricFilters[]|select(.filterPattern|test($re))|.filterName') fi if [[ $METRICFILTER_SET ]];then for metric in $METRICFILTER_SET; do - metric_name=$($AWSCLI logs describe-metric-filters $PROFILE_OPT --region "$CLOUDWATCH_LOGGROUP_REGION" --log-group-name "$group" --filter-name-prefix "$metric" --output text --query 'metricFilters[0].metricTransformations[0].metricName') + metric_name=$(echo $METRICFILTER_CACHE | jq -r --arg name $metric '.metricFilters[]|select(.filterName==$name)|.metricTransformations[0].metricName') HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region "$CLOUDWATCH_LOGGROUP_REGION" --query 'MetricAlarms[?MetricName==`'"$metric_name"'`]' --output text) if [[ $HAS_ALARM_ASSOCIATED ]];then - CHECK_OK="$CHECK_OK $group:$metric" + CHECK_OK="$CHECK_OK $CLOUDWATCH_LOGGROUP_NAME:$metric" else - CHECK_WARN="$CHECK_WARN $group:$metric" + CHECK_WARN="$CHECK_WARN $CLOUDWATCH_LOGGROUP_NAME:$metric" fi done - elif [ "$CLOUDWATCH_ACCOUNT_ID" == "$CURRENT_ACCOUNT_ID" ];then - CHECK_WARN="$CHECK_WARN $group" + elif [ "$CLOUDWATCH_LOGGROUP_ACCOUNT" == "$CURRENT_ACCOUNT_ID" ];then + CHECK_WARN="$CHECK_WARN $CLOUDWATCH_LOGGROUP_NAME" else - CHECK_CROSS_ACCOUNT_WARN="$CHECK_CROSS_ACCOUNT_WARN $group" + CHECK_CROSS_ACCOUNT_WARN="$CHECK_CROSS_ACCOUNT_WARN $CLOUDWATCH_LOGGROUP_NAME" fi done