From f32b76987eab4a19ba4bcc5039eac33dea1fa479 Mon Sep 17 00:00:00 2001 From: Tobi Fuhrimann Date: Fri, 16 Aug 2019 12:03:28 +0200 Subject: [PATCH] Make 3.x tests simpler and more useful --- checks/check31 | 46 +------------------------------------- checks/check310 | 23 +------------------ checks/check311 | 23 +------------------ checks/check312 | 23 +------------------ checks/check313 | 23 +------------------ checks/check314 | 23 +------------------ checks/check32 | 22 +----------------- checks/check33 | 22 +----------------- checks/check34 | 22 +----------------- checks/check35 | 22 +----------------- checks/check36 | 22 +----------------- checks/check37 | 22 +----------------- checks/check38 | 22 +----------------- checks/check39 | 22 +----------------- include/check3x | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ prowler | 1 + 16 files changed, 74 insertions(+), 323 deletions(-) create mode 100644 include/check3x diff --git a/checks/check31 b/checks/check31 index 2b7cb597b0b..13a11eb1e44 100644 --- a/checks/check31 +++ b/checks/check31 @@ -15,49 +15,5 @@ CHECK_TYPE_check31="LEVEL1" CHECK_ALTERNATE_check301="check31" check31(){ - # "Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text| tr ' ' ' -' | 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 ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - #METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | awk '/UnauthorizedOperation/ || /AccessDenied/ {print $3}') - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --output text | grep METRICFILTERS | awk 'BEGIN {IGNORECASE=1}; /UnauthorizedOperation/ || /AccessDenied/ {print $3};') - 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') - 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 - CHECK31OK="$CHECK31OK $group:$metric" - else - CHECK31WARN="$CHECK31WARN $group:$metric" - fi - done - else - CHECK31WARN="$CHECK31WARN $group" - fi - done - - if [[ $CHECK31OK ]]; then - for group in $CHECK31OK; do - metric=${group#*:} - group=${group%:*} - textPass "CloudWatch group $group found with metric filter $metric and alarms set for Unauthorized Operation and Access Denied" - done - fi - if [[ $CHECK31WARN ]]; then - for group in $CHECK31WARN; do - case $group in - *:*) metric=${group#*:} - group=${group%:*} - textFail "CloudWatch group $group found with metric filter $metric but no alarms associated" - ;; - *) textFail "CloudWatch group $group found but no metric filters or alarms associated" - esac - done - fi - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.errorCode\s*=\s*"\*UnauthorizedOperation".+\$\.errorCode\s*=\s*"AccessDenied\*"' } diff --git a/checks/check310 b/checks/check310 index 3f8f7db4e91..3c1e1d11243 100644 --- a/checks/check310 +++ b/checks/check310 @@ -15,26 +15,5 @@ CHECK_TYPE_check310="LEVEL2" CHECK_ALTERNATE_check310="check310" check310(){ - # "Ensure a log metric filter and alarm exist for security group changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /SecurityGroup/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for security group changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*CreateSecurityGroup.+\$\.eventName\s*=\s*DeleteSecurityGroup' } diff --git a/checks/check311 b/checks/check311 index 391b824c746..12aec273f45 100644 --- a/checks/check311 +++ b/checks/check311 @@ -15,26 +15,5 @@ CHECK_TYPE_check311="LEVEL2" CHECK_ALTERNATE_check311="check311" check311(){ - # "Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /NetworkAcl/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for changes to NACLs" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateNetworkAcl.+\$\.eventName\s*=\s*CreateNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclAssociation' } diff --git a/checks/check312 b/checks/check312 index a30987d6286..0bf516b6933 100644 --- a/checks/check312 +++ b/checks/check312 @@ -15,26 +15,5 @@ CHECK_TYPE_check312="LEVEL1" CHECK_ALTERNATE_check312="check312" check312(){ - # "Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /InternetGateway/ || /CustomerGateway/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for changes to network gateways" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateCustomerGateway.+\$\.eventName\s*=\s*DeleteCustomerGateway.+\$\.eventName\s*=\s*DeleteInternetGateway.+\$\.eventName\s*=\s*DetachInternetGateway' } diff --git a/checks/check313 b/checks/check313 index bccb8cbd66f..ca619ecc725 100644 --- a/checks/check313 +++ b/checks/check313 @@ -15,26 +15,5 @@ CHECK_TYPE_check313="LEVEL1" CHECK_ALTERNATE_check313="check313" check313(){ - # "Ensure a log metric filter and alarm exist for route table changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /Route/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for route table changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateRoute.+\$\.eventName\s*=\s*CreateRouteTable.+\$\.eventName\s*=\s*DeleteRoute.+\$\.eventName\s*=\s*DisassociateRouteTable' } diff --git a/checks/check314 b/checks/check314 index c04acf90a40..6c29c946320 100644 --- a/checks/check314 +++ b/checks/check314 @@ -15,26 +15,5 @@ CHECK_TYPE_check314="LEVEL1" CHECK_ALTERNATE_check314="check314" check314(){ - # "Ensure a log metric filter and alarm exist for VPC changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /VPC/i;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for VPC changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateVpc.+\$\.eventName\s*=\s*DeleteVpc.+\$\.eventName\s*=\s*DisableVpcClassicLink.+\$\.eventName\s*=\s*EnableVpcClassicLink' } diff --git a/checks/check32 b/checks/check32 index ff7fc05e034..e13b18dd577 100644 --- a/checks/check32 +++ b/checks/check32 @@ -15,25 +15,5 @@ CHECK_TYPE_check32="LEVEL1" CHECK_ALTERNATE_check302="check32" check32(){ - # "Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' |grep filterPattern|grep MFAUsed| awk '/ConsoleLogin/ && (/additionalEventData.MFAUsed.*\!=.*\"Yes/) {print $1}') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /ConsoleLogin/ || /MFAUsed/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms set for sign-in Console without MFA enabled" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*"ConsoleLogin".+\$\.additionalEventData\.MFAUsed\s*!=\s*"Yes"' } diff --git a/checks/check33 b/checks/check33 index 9d83b3dcdf6..b6825566c0b 100644 --- a/checks/check33 +++ b/checks/check33 @@ -15,25 +15,5 @@ CHECK_TYPE_check33="LEVEL1" CHECK_ALTERNATE_check303="check33" check33(){ - # "Ensure a log metric filter and alarm exist for usage of root account (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | tr '[:upper:]' '[:lower:]'| grep -Ei 'userIdentity|Root|AwsServiceEvent') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms set for usage of root account" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.userIdentity\.type\s*=\s*"Root".+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*"AwsServiceEvent"' } diff --git a/checks/check34 b/checks/check34 index 1883a823b17..4eaf345cd6a 100644 --- a/checks/check34 +++ b/checks/check34 @@ -15,25 +15,5 @@ CHECK_TYPE_check34="LEVEL1" CHECK_ALTERNATE_check304="check34" check34(){ - # "Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'DeleteGroupPolicy.*DeleteRolePolicy.*DeleteUserPolicy.*PutGroupPolicy.*PutRolePolicy.*PutUserPolicy.*CreatePolicy.*DeletePolicy.*CreatePolicyVersion.*DeletePolicyVersion.*AttachRolePolicy.*DetachRolePolicy.*AttachUserPolicy.*DetachUserPolicy.*AttachGroupPolicy.*DetachGroupPolicy') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /DeletePolicy/ || /DeletePolicies/ || /Policies/ || /Policy/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for IAM policy changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*DeleteGroupPolicy.+\$\.eventName\s*=\s*DeleteRolePolicy.+\$\.eventName\s*=\s*AttachGroupPolicy.+\$\.eventName\s*=\s*DetachGroupPolicy' } diff --git a/checks/check35 b/checks/check35 index afeb5229551..63ba2243cb9 100644 --- a/checks/check35 +++ b/checks/check35 @@ -15,25 +15,5 @@ CHECK_TYPE_check35="LEVEL1" CHECK_ALTERNATE_check305="check35" check35(){ - # "Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /TrailChange/ || /Trails/ || /CreateTrail/ || /UpdateTrail/ || /DeleteTrail/ || /StartLogging/ || /StopLogging/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for CloudTrail configuration changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateTrail.+\$\.eventName\s*=\s*UpdateTrail.+\$\.eventName\s*=\s*StartLogging.+\$\.eventName\s*=\s*StopLogging' } diff --git a/checks/check36 b/checks/check36 index c57712184aa..8fc6d86f5ee 100644 --- a/checks/check36 +++ b/checks/check36 @@ -15,25 +15,5 @@ CHECK_TYPE_check36="LEVEL2" CHECK_ALTERNATE_check306="check36" check36(){ - # "Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /FailedLogin/ || /ConsoleLogin/ || /Failed/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for AWS Management Console authentication failures" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*ConsoleLogin.+\$\.errorMessage\s*=\s*"Failed authentication"' } diff --git a/checks/check37 b/checks/check37 index 795b03291be..44e671135ed 100644 --- a/checks/check37 +++ b/checks/check37 @@ -15,25 +15,5 @@ CHECK_TYPE_check37="LEVEL2" CHECK_ALTERNATE_check307="check37" check37(){ - # "Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /DisableKey/ || /ScheduleKeyDeletion/ || /kms/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for changes of customer created CMKs" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventSource\s*=\s*kms.amazonaws.com.+\$\.eventName\s*=\s*DisableKey.+\$\.eventName\s*=\s*ScheduleKeyDeletion' } diff --git a/checks/check38 b/checks/check38 index bb0ccc7a494..f0188751324 100644 --- a/checks/check38 +++ b/checks/check38 @@ -15,25 +15,5 @@ CHECK_TYPE_check38="LEVEL1" CHECK_ALTERNATE_check308="check38" check38(){ - # "Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /S3/ || /BucketPolicy/ || /BucketPolicies/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for S3 bucket policy changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventSource\s*=\s*s3.amazonaws.com.+\$\.eventName\s*=\s*PutBucketAcl.+\$\.eventName\s*=\s*DeleteBucketLifecycle.+\$\.eventName\s*=\s*DeleteBucketReplication' } diff --git a/checks/check39 b/checks/check39 index 0f250a83976..d8feb0de29e 100644 --- a/checks/check39 +++ b/checks/check39 @@ -15,25 +15,5 @@ CHECK_TYPE_check39="LEVEL2" CHECK_ALTERNATE_check309="check39" check39(){ - # "Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | 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 | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /config/ || /ConfigurationRecorder/ || /DeliveryChannel/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for AWS Config configuration changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventSource\s*=\s*config.amazonaws.com.+\$\.eventName\s*=\s*StopConfigurationRecorder.+\$\.eventName\s*=\s*PutDeliveryChannel.+\$\.eventName\s*=\s*PutConfigurationRecorder' } diff --git a/include/check3x b/include/check3x new file mode 100644 index 00000000000..3b630330b87 --- /dev/null +++ b/include/check3x @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# Prowler - the handy cloud security tool (c) by Toni de la Fuente +# +# This Prowler check is licensed under a +# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. +# +# You should have received a copy of the license along with this +# work. If not, see . + +check3x(){ + grep_filter=$1 + local CHECK_OK + local CHECK_WARN + + CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text| tr ' ' ' +' | 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 ' ' ' +' | grep "$group" | awk -F: '{ print $4 }' | head -n 1) + 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 [[ $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') + 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" + else + CHECK_WARN="$CHECK_WARN $group:$metric" + fi + done + else + CHECK_WARN="$CHECK_WARN $group" + fi + done + + if [[ $CHECK_OK ]]; then + for group in $CHECK_OK; do + metric=${group#*:} + group=${group%:*} + textPass "CloudWatch group $group found with metric filter $metric and alarms set" + done + fi + if [[ $CHECK_WARN ]]; then + for group in $CHECK_WARN; do + case $group in + *:*) metric=${group#*:} + group=${group%:*} + textFail "CloudWatch group $group found with metric filter $metric but no alarms associated" + ;; + *) textFail "CloudWatch group $group found but no metric filters or alarms associated" + esac + done + fi + else + textFail "No CloudWatch group found for CloudTrail events" + fi +} diff --git a/prowler b/prowler index ddc56bde390..7f92e7b1e22 100755 --- a/prowler +++ b/prowler @@ -165,6 +165,7 @@ done . $PROWLER_DIR/include/scoring . $PROWLER_DIR/include/python_detector . $PROWLER_DIR/include/secrets_detector +. $PROWLER_DIR/include/check3x # Get a list of all available AWS Regions REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' \