From f5ebc1d22791d183ab63693de25e343e7ce70888 Mon Sep 17 00:00:00 2001 From: Doug Byrne Date: Wed, 8 Nov 2017 16:13:26 -0500 Subject: [PATCH 01/61] Better grep pattern to find users in the IAM report --- prowler | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/prowler b/prowler index 672c391d..6c570ebd 100755 --- a/prowler +++ b/prowler @@ -528,7 +528,7 @@ check14(){ # textWarn "Users with access key 1 older than 90 days:" for user in $LIST_OF_USERS_WITH_ACCESS_KEY1; do # check access key 1 - DATEROTATED1=$(cat $TEMP_REPORT_FILE | grep -v user_creation_time | grep $user| awk -F, '{ print $10 }' | grep -v "N/A" | awk -F"T" '{ print $1 }') + DATEROTATED1=$(cat $TEMP_REPORT_FILE | grep -v user_creation_time | grep "^${user},"| awk -F, '{ print $10 }' | grep -v "N/A" | awk -F"T" '{ print $1 }') HOWOLDER=$(how_older_from_today $DATEROTATED1) if [ $HOWOLDER -gt "90" ];then @@ -547,7 +547,7 @@ check14(){ # textWarn "Users with access key 2 older than 90 days:" for user in $LIST_OF_USERS_WITH_ACCESS_KEY2; do # check access key 2 - DATEROTATED2=$(cat $TEMP_REPORT_FILE | grep -v user_creation_time | grep $user| awk -F, '{ print $10 }' | grep -v "N/A" | awk -F"T" '{ print $1 }') + DATEROTATED2=$(cat $TEMP_REPORT_FILE | grep -v user_creation_time | grep "^${user},"| awk -F, '{ print $10 }' | grep -v "N/A" | awk -F"T" '{ print $1 }') HOWOLDER=$(how_older_from_today $DATEROTATED2) if [ $HOWOLDER -gt "90" ];then textWarn " $user has not rotated access key2. " @@ -817,8 +817,8 @@ check123(){ textTitle "$ID123" "$TITLE123" "NOT_SCORED" "LEVEL1" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text $PROFILE_OPT --region $REGION) # List of USERS with KEY1 last_used_date as N/A - LIST_USERS_KEY1_NA=$(for user in $LIST_USERS; do grep $user $TEMP_REPORT_FILE|awk -F, '{ print $1,$11 }'|grep N/A |awk '{ print $1 }'; done) - LIST_USERS_KEY1_ACTIVE=$(for user in $LIST_USERS_KEY1_NA; do grep $user $TEMP_REPORT_FILE|awk -F, '{ print $1,$9 }'|grep "true$"|awk '{ print $1 }'|sed 's/[[:blank:]]+/,/g' ; done) + LIST_USERS_KEY1_NA=$(for user in $LIST_USERS; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$11 }'|grep N/A |awk '{ print $1 }'; done) + LIST_USERS_KEY1_ACTIVE=$(for user in $LIST_USERS_KEY1_NA; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$9 }'|grep "true$"|awk '{ print $1 }'|sed 's/[[:blank:]]+/,/g' ; done) if [[ $LIST_USERS_KEY1_ACTIVE ]]; then for user in $LIST_USERS_KEY1_ACTIVE; do textNotice "$user has never used Access Key 1" @@ -827,8 +827,8 @@ check123(){ textOK "No users found with Access Key 1 never used" fi # List of USERS with KEY2 last_used_date as N/A - LIST_USERS_KEY2_NA=$(for user in $LIST_USERS; do grep $user $TEMP_REPORT_FILE|awk -F, '{ print $1,$16 }'|grep N/A |awk '{ print $1 }' ; done) - LIST_USERS_KEY2_ACTIVE=$(for user in $LIST_USERS_KEY2_NA; do grep $user $TEMP_REPORT_FILE|awk -F, '{ print $1,$14 }'|grep "true$" |awk '{ print $1 }' ; done) + LIST_USERS_KEY2_NA=$(for user in $LIST_USERS; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$16 }'|grep N/A |awk '{ print $1 }' ; done) + LIST_USERS_KEY2_ACTIVE=$(for user in $LIST_USERS_KEY2_NA; do grep "^${user}," $TEMP_REPORT_FILE|awk -F, '{ print $1,$14 }'|grep "true$" |awk '{ print $1 }' ; done) if [[ $LIST_USERS_KEY2_ACTIVE ]]; then for user in $LIST_USERS_KEY2_ACTIVE; do textNotice "$user has never used Access Key 2" From 482d3c5a7786feb1de4a031d4bd8af3a6b964e25 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Fri, 17 Nov 2017 18:17:23 -0500 Subject: [PATCH 02/61] fixed #111 and improved section 3 --- prowler | 282 +++++++++++++++++++++++++++++++++++++++----------------- trails | 26 ++++++ 2 files changed, 223 insertions(+), 85 deletions(-) create mode 100644 trails diff --git a/prowler b/prowler index 7aa6bdbf..cb5f5ab8 100755 --- a/prowler +++ b/prowler @@ -1038,12 +1038,20 @@ check31(){ textTitle "$ID31" "$TITLE31" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep AccessDenied) - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters for Access Denied enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | awk '/UnauthorizedOperation/ || /AccessDenied/ {print $1" "$2}') + 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}; /UnauthorizedOperation/ || /AccessDenied/;') + if [[ $HAS_ALARM_ASSOCIATED ]];then + textOK "CloudWatch group $group found with metric filters and alarms set for Unauthorized Operation and Access Denied" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1055,12 +1063,20 @@ check32(){ textTitle "$ID32" "$TITLE32" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' |grep filterPattern|grep MFAUsed| awk '/ConsoleLogin/ && (/additionalEventData.MFAUsed.*\!=.*\"Yes/) {print $1}') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters for sign-in Console without MFA enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms set for sign-in Console without MFA enabled" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1072,12 +1088,20 @@ check33(){ textTitle "$ID33" "$TITLE33" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters for usage of root account enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 | awk 'BEGIN {IGNORECASE=1}; /userIdentity/ || /Root/ || /AwsServiceEvent/;') + if [[ $HAS_ALARM_ASSOCIATED ]];then + textOK "CloudWatch group $group found with metric filters and alarms set for usage of root account" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1089,12 +1113,20 @@ check34(){ textTitle "$ID34" "$TITLE34" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $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 - textOK "CloudWatch group found with metric filters for IAM policy changes enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms for IAM policy changes" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1106,12 +1138,20 @@ check35(){ textTitle "$ID35" "$TITLE35" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters for CloudTrail configuration changes enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms for CloudTrail configuration changes" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1123,12 +1163,20 @@ check36(){ textTitle "$ID36" "$TITLE36" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters for usage of root account enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms for usage of root account" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1140,12 +1188,20 @@ check37(){ textTitle "$ID37" "$TITLE37" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms for changes of customer created CMKs" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1157,12 +1213,20 @@ check38(){ textTitle "$ID38" "$TITLE38" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms for S3 bucket policy changes" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1174,12 +1238,20 @@ check39(){ textTitle "$ID39" "$TITLE39" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms for AWS Config configuration changes" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1191,12 +1263,20 @@ check310(){ textTitle "$ID310" "$TITLE310" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms for security group changes" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1208,12 +1288,20 @@ check311(){ textTitle "$ID311" "$TITLE311" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms for changes to NACLs" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1225,12 +1313,20 @@ check312(){ textTitle "$ID312" "$TITLE312" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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}; /NetworkAcl/;') + if [[ $HAS_ALARM_ASSOCIATED ]];then + textOK "CloudWatch group $group found with metric filters and alarms for changes to network gateways" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1242,12 +1338,20 @@ check313(){ textTitle "$ID313" "$TITLE313" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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 + textOK "CloudWatch group $group found with metric filters and alarms for route table changes" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1259,12 +1363,20 @@ check314(){ textTitle "$ID314" "$TITLE314" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP $PROFILE_OPT --region $REGION --query 'metricFilters' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink') - if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found with metric filters enabled" - else - textWarn "CloudWatch group found but no metric filters or alarms associated" - fi + 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 }') + 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/;') + if [[ $HAS_ALARM_ASSOCIATED ]];then + textOK "CloudWatch group $group found with metric filters and alarms for VPC changes" + else + textWarn "CloudWatch group $group found with metric filters but no alarms associated" + fi + else + textWarn "CloudWatch group $group found but no metric filters or alarms associated" + fi + done else textWarn "No CloudWatch group found for CloudTrail events" fi @@ -1295,7 +1407,7 @@ check315(){ textNotice "Region $regx / Topic $TOPIC_SHORT / Subscription $dest" "$regx" done else - textWarn "Region $regx / Topic $TOPIC_SHORT / Subscription NONE NONE" "$regx" + textWarn "Region $regx / Topic $TOPIC_SHORT / Subscription NONE" "$regx" fi done elif [[ $CAN_SNS_LIST_SUBS -eq 0 ]]; then diff --git a/trails b/trails new file mode 100644 index 00000000..5c0681d4 --- /dev/null +++ b/trails @@ -0,0 +1,26 @@ +{ + "trailList": [ + { + "IncludeGlobalServiceEvents": true, + "Name": "AWSMacieTrail-DO-NOT-EDIT", + "TrailARN": "arn:aws:cloudtrail:us-east-1:623229943871:trail/AWSMacieTrail-DO-NOT-EDIT", + "LogFileValidationEnabled": false, + "IsMultiRegionTrail": false, + "HasCustomEventSelectors": true, + "S3BucketName": "623229943871-awsmacietrail-dataevent", + "HomeRegion": "us-east-1" + }, + { + "IncludeGlobalServiceEvents": true, + "Name": "Default", + "TrailARN": "arn:aws:cloudtrail:us-east-1:623229943871:trail/Default", + "LogFileValidationEnabled": true, + "IsMultiRegionTrail": true, + "HasCustomEventSelectors": false, + "S3BucketName": "sec623229943871", + "CloudWatchLogsRoleArn": "arn:aws:iam::623229943871:role/CloudTrail_CloudWatchLogs_Role", + "CloudWatchLogsLogGroupArn": "arn:aws:logs:us-east-1:623229943871:log-group:CloudTrail/DefaultLogGroup:*", + "HomeRegion": "us-east-1" + } + ] +} From 521088aa5567e879e320561b97ea391e1b81034e Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Fri, 17 Nov 2017 18:20:42 -0500 Subject: [PATCH 03/61] fixed #111 and improved section 3 --- trails | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 trails diff --git a/trails b/trails deleted file mode 100644 index 5c0681d4..00000000 --- a/trails +++ /dev/null @@ -1,26 +0,0 @@ -{ - "trailList": [ - { - "IncludeGlobalServiceEvents": true, - "Name": "AWSMacieTrail-DO-NOT-EDIT", - "TrailARN": "arn:aws:cloudtrail:us-east-1:623229943871:trail/AWSMacieTrail-DO-NOT-EDIT", - "LogFileValidationEnabled": false, - "IsMultiRegionTrail": false, - "HasCustomEventSelectors": true, - "S3BucketName": "623229943871-awsmacietrail-dataevent", - "HomeRegion": "us-east-1" - }, - { - "IncludeGlobalServiceEvents": true, - "Name": "Default", - "TrailARN": "arn:aws:cloudtrail:us-east-1:623229943871:trail/Default", - "LogFileValidationEnabled": true, - "IsMultiRegionTrail": true, - "HasCustomEventSelectors": false, - "S3BucketName": "sec623229943871", - "CloudWatchLogsRoleArn": "arn:aws:iam::623229943871:role/CloudTrail_CloudWatchLogs_Role", - "CloudWatchLogsLogGroupArn": "arn:aws:logs:us-east-1:623229943871:log-group:CloudTrail/DefaultLogGroup:*", - "HomeRegion": "us-east-1" - } - ] -} From 2c86cc04d58f36dc4348354f0716da2f7957fdf8 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Fri, 17 Nov 2017 18:45:20 -0500 Subject: [PATCH 04/61] Added exit code enhancement #126 --- prowler | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/prowler b/prowler index cb5f5ab8..36d4ba7f 100755 --- a/prowler +++ b/prowler @@ -37,6 +37,7 @@ MONOCHROME=0 MODE="text" SEP=',' KEEPCREDREPORT=0 +EXITCODE=0 # Command usage menu @@ -62,7 +63,8 @@ while getopts ":hkp:r:c:f:m:M:n" OPTION; do case $OPTION in h ) usage - exit 1 + EXITCODE=1 + exit $EXITCODE ;; k ) KEEPCREDREPORT=1 @@ -92,13 +94,15 @@ while getopts ":hkp:r:c:f:m:M:n" OPTION; do echo "" echo "$OPTRED ERROR!$OPTNORMAL -$OPTARG requires an argument" usage - exit 1 + EXITCODE=1 + exit $EXITCODE ;; ? ) echo "" echo "$OPTRED ERROR!$OPTNORMAL Invalid option" usage - exit 1 + EXITCODE=1 + exit $EXITCODE ;; esac done @@ -107,7 +111,8 @@ if [[ $MODE != "mono" && $MODE != "text" && $MODE != "csv" ]]; then echo "" echo "$OPTRED ERROR!$OPTNORMAL Invalid output mode. Choose text, mono, or csv." usage - exit 1 + EXITCODE=1 + exit $EXITCODE fi if [[ $MODE == "mono" || $MODE == "csv" ]]; then @@ -226,7 +231,8 @@ elif [[ "$OSTYPE" == "cygwin" ]]; then } else echo "Unknown Operating System" - exit + EXITCODE=1 + exit $EXITCODE fi # Check environment if profile provided reads it from creds file, then instance profile @@ -260,7 +266,8 @@ fi AWSCLI=$(which aws) if [ -z "${AWSCLI}" ]; then echo -e "\n$RED ERROR!$NORMAL AWS-CLI (aws command) not found. Make sure it is installed correctly and in your \$PATH\n" - exit + EXITCODE=1 + exit $EXITCODE fi TITLE_ID="" @@ -293,6 +300,7 @@ textNotice(){ } textWarn(){ + EXITCODE=3 if [[ $MODE == "csv" ]]; then if [[ $2 ]]; then REPREGION=$2 @@ -371,7 +379,8 @@ getWhoami(){ # Failed to get own identity ... exit echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!" >&2 echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!" - exit 2 + EXITCODE=2 + exit $EXITCODE fi CALLER_ARN=$(echo $CALLER_ARN_RAW | tr -d '"') printCsvHeader @@ -397,10 +406,11 @@ getWhoami(){ $AWSCLI sts get-caller-identity --output table $PROFILE_OPT --region $REGION if [[ 255 -eq $? ]]; then # Failed to get own identity ... exit - echo variableeeee $PROFILE_OPT - echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!x" + echo variable $PROFILE_OPT + echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!" >&2 echo "ERROR WITH $PROFILE CREDENTIALS - EXITING!" - exit 2 + EXITCODE=2 + exit $EXITCODE fi echo "" fi @@ -1741,7 +1751,7 @@ callCheck(){ textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; esac cleanTemp - exit + exit $EXITCODE fi } @@ -1833,3 +1843,4 @@ extra74 extra75 cleanTemp +exit $EXITCODE From 010dd950b3e820e984a533c44fd7ece3eea09a1d Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 21 Nov 2017 10:20:38 -0500 Subject: [PATCH 05/61] fixed issue with multiple CLOUDWATCH_GROUPS in section 3. Issue #111 --- prowler | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/prowler b/prowler index 36d4ba7f..764d6fc5 100755 --- a/prowler +++ b/prowler @@ -1046,7 +1046,7 @@ check31(){ ID31="3.1,3.01" TITLE31="Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" textTitle "$ID31" "$TITLE31" "SCORED" "LEVEL1" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1071,7 +1071,7 @@ check32(){ ID32="3.2,3.02" TITLE32="Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" textTitle "$ID32" "$TITLE32" "SCORED" "LEVEL1" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1096,7 +1096,7 @@ check33(){ ID33="3.3,3.03" TITLE33="Ensure a log metric filter and alarm exist for usage of root account (Scored)" textTitle "$ID33" "$TITLE33" "SCORED" "LEVEL1" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1121,7 +1121,7 @@ check34(){ ID34="3.4,3.04" TITLE34="Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" textTitle "$ID34" "$TITLE34" "SCORED" "LEVEL1" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1146,7 +1146,7 @@ check35(){ ID35="3.5,3.05" TITLE35="Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" textTitle "$ID35" "$TITLE35" "SCORED" "LEVEL1" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1171,7 +1171,7 @@ check36(){ ID36="3.6,3.06" TITLE36="Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" textTitle "$ID36" "$TITLE36" "SCORED" "LEVEL2" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1196,7 +1196,7 @@ check37(){ ID37="3.7,3.07" TITLE37="Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" textTitle "$ID37" "$TITLE37" "SCORED" "LEVEL2" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1221,7 +1221,7 @@ check38(){ ID38="3.8,3.08" TITLE38="Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" textTitle "$ID38" "$TITLE38" "SCORED" "LEVEL1" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1246,7 +1246,7 @@ check39(){ ID39="3.9,3.09" TITLE39="Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" textTitle "$ID39" "$TITLE39" "SCORED" "LEVEL2" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1271,7 +1271,7 @@ check310(){ ID310="3.10" TITLE310="Ensure a log metric filter and alarm exist for security group changes (Scored)" textTitle "$ID310" "$TITLE310" "SCORED" "LEVEL2" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1296,7 +1296,7 @@ check311(){ ID311="3.11" TITLE311="Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" textTitle "$ID311" "$TITLE311" "SCORED" "LEVEL2" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1321,7 +1321,7 @@ check312(){ ID312="3.12" TITLE312="Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" textTitle "$ID312" "$TITLE312" "SCORED" "LEVEL1" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1346,7 +1346,7 @@ check313(){ ID313="3.13" TITLE313="Ensure a log metric filter and alarm exist for route table changes (Scored)" textTitle "$ID313" "$TITLE313" "SCORED" "LEVEL1" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') @@ -1371,7 +1371,7 @@ check314(){ ID314="3.14" TITLE314="Ensure a log metric filter and alarm exist for VPC changes (Scored)" textTitle "$ID314" "$TITLE314" "SCORED" "LEVEL1" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $7 }') + CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') From 0ff6b7fd708347ddea123bc8d398dc50b013eb57 Mon Sep 17 00:00:00 2001 From: Pascal Bugnion Date: Wed, 22 Nov 2017 13:03:42 +0000 Subject: [PATCH 06/61] Avoid name clashes on word boundaries --- prowler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler b/prowler index 29d04358..679c726b 100755 --- a/prowler +++ b/prowler @@ -484,7 +484,7 @@ check12(){ COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$4 }' |grep true | awk '{ print $1 }') COMMAND12=$( for i in $COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED; do - cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$8 }' |grep -w $i| grep false | awk '{ print $1 }' + cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$8 }' |grep "$i " |grep false | awk '{ print $1 }' done) textTitle "$ID12" "$TITLE12" "SCORED" "LEVEL1" if [[ $COMMAND12 ]]; then From 4b7b28eb873c7b9ddebc05ea31456c771a35d743 Mon Sep 17 00:00:00 2001 From: Steve Neuharth Date: Mon, 27 Nov 2017 20:27:38 -0600 Subject: [PATCH 07/61] fixed internet gateway check --- prowler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler b/prowler index b40ddf3a..163e302e 100755 --- a/prowler +++ b/prowler @@ -1327,7 +1327,7 @@ check312(){ CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $4 }') 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}; /NetworkAcl/;') + 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 textOK "CloudWatch group $group found with metric filters and alarms for changes to network gateways" else From d8879d90853fe929971c923852f4422244f4ea3f Mon Sep 17 00:00:00 2001 From: Pascal Bugnion Date: Wed, 6 Dec 2017 14:30:25 +0000 Subject: [PATCH 08/61] Fix bug getting policy versions When serveral policies have the same full word substring, getting the policy versions can return multiple entries. Now fixed. --- prowler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler b/prowler index 163e302e..3614a095 100755 --- a/prowler +++ b/prowler @@ -856,7 +856,7 @@ check124(){ if [[ $LIST_CUSTOM_POLICIES ]]; then textNotice "Looking for custom policies: (skipping default policies - it may take few seconds...)" for policy in $LIST_CUSTOM_POLICIES; do - POLICY_VERSION=$($AWSCLI iam list-policies $PROFILE_OPT --region $REGION --query 'Policies[*].[Arn,DefaultVersionId]' --output text|grep -w $policy |awk '{ print $2}') + POLICY_VERSION=$($AWSCLI iam list-policies $PROFILE_OPT --region $REGION --query 'Policies[*].[Arn,DefaultVersionId]' --output text |awk "\$1 == \"$policy\" { print \$2 }") POLICY_WITH_FULL=$($AWSCLI iam get-policy-version --output text --policy-arn $policy --version-id $POLICY_VERSION --query "PolicyVersion.Document.Statement[?Effect == 'Allow' && contains(Resource, '*') && contains (Action, '*')]" $PROFILE_OPT --region $REGION) if [[ $POLICY_WITH_FULL ]]; then POLICIES_ALLOW_LIST="$POLICIES_ALLOW_LIST $policy" From 4e53521c59b7894eabd1ba8f9d637577ead49cfd Mon Sep 17 00:00:00 2001 From: Stuart Powers Date: Thu, 7 Dec 2017 08:06:04 -0500 Subject: [PATCH 09/61] Support "" (blank) values to if [[ ]] statements --- prowler | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/prowler b/prowler index 3614a095..df30470c 100755 --- a/prowler +++ b/prowler @@ -115,7 +115,7 @@ if [[ $MODE != "mono" && $MODE != "text" && $MODE != "csv" ]]; then exit $EXITCODE fi -if [[ $MODE == "mono" || $MODE == "csv" ]]; then +if [[ "$MODE" == "mono" || "$MODE" == "csv" ]]; then MONOCHROME=1 fi @@ -274,7 +274,7 @@ TITLE_ID="" TITLE_TEXT="CALLER ERROR - UNSET TITLE" ## Output formatting functions textOK(){ - if [[ $MODE == "csv" ]]; then + if [[ "$MODE" == "csv" ]]; then if [[ $2 ]]; then REPREGION=$2 else @@ -287,7 +287,7 @@ textOK(){ } textNotice(){ - if [[ $MODE == "csv" ]]; then + if [[ "$MODE" == "csv" ]]; then if [[ $2 ]]; then REPREGION=$2 else @@ -301,7 +301,7 @@ textNotice(){ textWarn(){ EXITCODE=3 - if [[ $MODE == "csv" ]]; then + if [[ "$MODE" == "csv" ]]; then if [[ $2 ]]; then REPREGION=$2 else @@ -343,10 +343,10 @@ textTitle(){ *) ITEM_LEVEL="Unspecified or Invalid";; esac - if [[ $MODE == "csv" ]]; then + if [[ "$MODE" == "csv" ]]; then >&2 echo "$TITLE_ID $TITLE_TEXT" else - if [[ $ITEM_SCORED == "Scored" ]]; then + if [[ "$ITEM_SCORED" == "Scored" ]]; then echo -e "\n$BLUE $TITLE_ID $NORMAL $TITLE_TEXT" else echo -e "\n$PURPLE $TITLE_ID $TITLE_TEXT $NORMAL" @@ -373,7 +373,7 @@ prowlerBanner() { # Get whoami in AWS, who is the user running this shell script getWhoami(){ ACCOUNT_NUM=$($AWSCLI sts get-caller-identity --output json $PROFILE_OPT --region $REGION --query "Account" | tr -d '"') - if [[ $MODE == "csv" ]]; then + if [[ "$MODE" == "csv" ]]; then CALLER_ARN_RAW=$($AWSCLI sts get-caller-identity --output json $PROFILE_OPT --region $REGION --query "Arn") if [[ 255 -eq $? ]]; then # Failed to get own identity ... exit @@ -577,7 +577,7 @@ check15(){ TITLE15="Ensure IAM password policy requires at least one uppercase letter (Scored)" COMMAND15=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireUppercaseCharacters' 2> /dev/null) # must be true textTitle "$ID15" "$TITLE15" "SCORED" "LEVEL1" - if [[ $COMMAND15 == "true" ]];then + if [[ "$COMMAND15" == "true" ]];then textOK "Password Policy requires upper case" else textWarn "Password Policy missing upper-case requirement" @@ -589,7 +589,7 @@ check16(){ TITLE16="Ensure IAM password policy require at least one lowercase letter (Scored)" COMMAND16=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireLowercaseCharacters' 2> /dev/null) # must be true textTitle "$ID16" "$TITLE16" "SCORED" "LEVEL1" - if [[ $COMMAND16 == "true" ]];then + if [[ "$COMMAND16" == "true" ]];then textOK "Password Policy requires lower case" else textWarn "Password Policy missing lower-case requirement" @@ -601,7 +601,7 @@ check17(){ TITLE17="Ensure IAM password policy require at least one symbol (Scored)" COMMAND17=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireSymbols' 2> /dev/null) # must be true textTitle "$ID17" "$TITLE17" "SCORED" "LEVEL1" - if [[ $COMMAND17 == "true" ]];then + if [[ "$COMMAND17" == "true" ]];then textOK "Password Policy requires symbol" else textWarn "Password Policy missing symbol requirement" @@ -613,7 +613,7 @@ check18(){ TITLE18="Ensure IAM password policy require at least one number (Scored)" COMMAND18=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireNumbers' 2> /dev/null) # must be true textTitle "$ID18" "$TITLE18" "SCORED" "LEVEL1" - if [[ $COMMAND18 == "true" ]];then + if [[ "$COMMAND18" == "true" ]];then textOK "Password Policy requires number" else textWarn "Password Policy missing number requirement" @@ -654,7 +654,7 @@ check111(){ COMMAND111=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json | grep MaxPasswordAge | awk -F: '{ print $2 }'|sed 's/\ //g'|sed 's/,/ /g' 2> /dev/null) textTitle "$ID111" "$TITLE111" "SCORED" "LEVEL1" if [[ $COMMAND111 ]];then - if [ $COMMAND111 == "90" ];then + if [ "$COMMAND111" == "90" ];then textOK "Password Policy includes expiration" fi else @@ -669,12 +669,12 @@ check112(){ ROOTKEY1=$(cat $TEMP_REPORT_FILE |grep root_account|awk -F',' '{ print $9 }') ROOTKEY2=$(cat $TEMP_REPORT_FILE |grep root_account|awk -F',' '{ print $14 }') textTitle "$ID112" "$TITLE112" "SCORED" "LEVEL1" - if [ $ROOTKEY1 == "false" ];then + if [ "$ROOTKEY1" == "false" ];then textOK "No access key 1 found for root" else textWarn "Found access key 1 for root " fi - if [ $ROOTKEY2 == "false" ];then + if [ "$ROOTKEY2" == "false" ];then textOK "No access key 2 found for root" else textWarn "Found access key 2 for root " @@ -686,7 +686,7 @@ check113(){ TITLE113="Ensure MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json|grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') textTitle "$ID113" "$TITLE113" "SCORED" "LEVEL1" - if [ $COMMAND113 == "1" ]; then + if [ "$COMMAND113" == "1" ]; then textOK "Virtual MFA is enabled for root" else textWarn "MFA is not ENABLED for root account " @@ -698,9 +698,9 @@ check114(){ TITLE114="Ensure hardware MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json|grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') textTitle "$ID114" "$TITLE114" "SCORED" "LEVEL1" - if [ $COMMAND113 == "1" ]; then + if [ "$COMMAND113" == "1" ]; then COMMAND114=$($AWSCLI iam list-virtual-mfa-devices $PROFILE_OPT --region $REGION --query 'VirtualMFADevices' --output text|grep :root |wc -l) - if [ $COMMAND114 == "1" ]; then + if [ "$COMMAND114" == "1" ]; then textOK "Virtual MFA is enabled for root" else textOK "Hardware MFA is enabled for root " @@ -883,7 +883,7 @@ check21(){ if [[ $LIST_OF_TRAILS ]];then for trail in $LIST_OF_TRAILS;do MULTIREGION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].IsMultiRegionTrail' --output text --trail-name-list $trail) - if [[ $MULTIREGION_TRAIL_STATUS == 'False' ]];then + if [[ "$MULTIREGION_TRAIL_STATUS" == 'False' ]];then textWarn "$trail trail in $REGION is not enabled in multi region mode" else textOK "$trail trail in $REGION is enabled for all regions" @@ -902,7 +902,7 @@ check22(){ if [[ $LIST_OF_TRAILS ]];then for trail in $LIST_OF_TRAILS;do LOGFILEVALIDATION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].LogFileValidationEnabled' --output text --trail-name-list $trail) - if [[ $LOGFILEVALIDATION_TRAIL_STATUS == 'False' ]];then + if [[ "$LOGFILEVALIDATION_TRAIL_STATUS" == 'False' ]];then textWarn "$trail trail in $REGION has not log file validation enabled" else textOK "$trail trail in $REGION has log file validation enabled" @@ -1021,14 +1021,14 @@ check28(){ CHECK_KMS_KEYLIST_NO_DEFAULT=$(for key in $CHECK_KMS_KEYLIST ; do $AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --output text|grep -v 'Default master key that protects my ACM private keys when no other key is defined'|awk '{ print $3 }'|awk -F'/' '{ print $2 }'; done) for key in $CHECK_KMS_KEYLIST_NO_DEFAULT; do CHECK_KMS_KEY_TYPE=$($AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --query 'KeyMetadata.Origin' | sed 's/["]//g') - if [[ $CHECK_KMS_KEY_TYPE == "EXTERNAL" ]];then + if [[ "$CHECK_KMS_KEY_TYPE" == "EXTERNAL" ]];then textOK "Key $key in Region $regx Customer Uploaded Key Material." "$regx" else CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key $PROFILE_OPT --region $regx --output text) #CHECK_KMS_DEFAULT_KEY=$($AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --query 'KeyMetadata.Description' | sed -n '/Default master key that protects my ACM private keys when no other key is defined /p'|| echo "False") - if [[ $CHECK_KMS_KEY_ROTATION == "True" ]];then + if [[ "$CHECK_KMS_KEY_ROTATION" == "True" ]];then textOK "Key $key in Region $regx is set correctly" - elif [[ $CHECK_KMS_KEY_ROTATION == "False" && $CHECK_KMS_DEFAULT_KEY ]];then + elif [[ "$CHECK_KMS_KEY_ROTATION" == "False" && $CHECK_KMS_DEFAULT_KEY ]];then textNotice "Region $regx key $key is an AWS default master key and cannot be deleted nor modified." "$regx" else textWarn "Key $key in Region $regx is not set to rotate!!!" "$regx" @@ -1844,3 +1844,4 @@ extra75 cleanTemp exit $EXITCODE + From 887805c5be63cf1f7b29e7702735dfc99a304b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=CC=81s=CC=8C=20Milata?= Date: Tue, 12 Dec 2017 17:11:56 +0000 Subject: [PATCH 10/61] Use a query to get AccountMFAEnabled rather than awk/sed Parsing with awk/sed relies on the json being pretty printed (no other values on the same line), which is not always true, causing false-positive warings sometimes. Querying for SummaryMap.AccountMFAEnabled directly should be more robust. --- prowler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler b/prowler index df30470c..ec859b9c 100755 --- a/prowler +++ b/prowler @@ -684,7 +684,7 @@ check112(){ check113(){ ID113="1.13" TITLE113="Ensure MFA is enabled for the root account (Scored)" - COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json|grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') + COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled') textTitle "$ID113" "$TITLE113" "SCORED" "LEVEL1" if [ "$COMMAND113" == "1" ]; then textOK "Virtual MFA is enabled for root" From b67ca429e96c73dc9bc4fa08e183482b0b472065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=CC=81s=CC=8C=20Milata?= Date: Wed, 13 Dec 2017 13:38:27 +0000 Subject: [PATCH 11/61] Use a query to get AccountMFAEnabled rather than awk/sed Parsing with awk/sed relies on the json being pretty printed (no other values on the same line), which is not always true, causing false-positive warings sometimes. Querying for SummaryMap.AccountMFAEnabled directly should be more robust --- prowler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler b/prowler index ec859b9c..6ec5d7f9 100755 --- a/prowler +++ b/prowler @@ -696,7 +696,7 @@ check113(){ check114(){ ID114="1.14" TITLE114="Ensure hardware MFA is enabled for the root account (Scored)" - COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json|grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') + COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled') textTitle "$ID114" "$TITLE114" "SCORED" "LEVEL1" if [ "$COMMAND113" == "1" ]; then COMMAND114=$($AWSCLI iam list-virtual-mfa-devices $PROFILE_OPT --region $REGION --query 'VirtualMFADevices' --output text|grep :root |wc -l) From 07635ce57915c5904578250ea07a276bba5cb51f Mon Sep 17 00:00:00 2001 From: wassies <33995140+wassies@users.noreply.github.com> Date: Mon, 18 Dec 2017 10:31:29 +1000 Subject: [PATCH 12/61] Text change for issue #133 Updated OK text for issue #133 --- prowler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler b/prowler index 6ec5d7f9..b8f236a3 100755 --- a/prowler +++ b/prowler @@ -1179,7 +1179,7 @@ check36(){ 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 - textOK "CloudWatch group $group found with metric filters and alarms for usage of root account" + textOK "CloudWatch group $group found with metric filters and alarms for AWS Management Console authentication failures" else textWarn "CloudWatch group $group found with metric filters but no alarms associated" fi From 785633cc3b589147a28e2c544670abf49780ddba Mon Sep 17 00:00:00 2001 From: Steve Neuharth Date: Sun, 24 Dec 2017 09:27:45 -0600 Subject: [PATCH 13/61] add simple docker file --- Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..8b918078 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM python +MAINTAINER Steve Neuharth +RUN apt-get update && apt-get upgrade -y && pip install awscli ansi2html +ADD prowler* /usr/local/bin/ From e6fa8dd92d4d402fabddde5d02bf239a506f8650 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 11:47:49 -0500 Subject: [PATCH 14/61] added region info to help output --- prowler | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/prowler b/prowler index b8f236a3..e8e82b24 100755 --- a/prowler +++ b/prowler @@ -47,7 +47,7 @@ USAGE: `basename $0` -p -r [ -h ] Options: -p specify your AWS profile to use (i.e.: default) - -r specify an AWS region to direct API requests to (i.e.: us-east-1) + -r specify an AWS region to direct API requests to (i.e.: us-east-1), all regions are checked anyway -c specify a check number or group from the AWS CIS benchmark (i.e.: check11 for check 1.1, extra71, check3 for entire section 3 or level1 for CIS Level 1 Profile Definitions) -f specify an AWS region to run checks against (i.e.: us-west-1) -m specify the maximum number of items to return for long-running requests (default: 100) @@ -1844,4 +1844,3 @@ extra75 cleanTemp exit $EXITCODE - From 17b0df305370d716d6ca5fab308d58aa9e43900a Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 11:49:07 -0500 Subject: [PATCH 15/61] added region info to help output --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b365e477..76de37dd 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ USAGE: prowler -p -r [ -h ] Options: -p specify your AWS profile to use (i.e.: default) - -r specify an AWS region to direct API requests to (i.e.: us-east-1) + -r specify an AWS region to direct API requests to (i.e.: us-east-1), all regions are checked anyway -c specify a check number or group from the AWS CIS benchmark (i.e.: check11 for check 1.1, check3 for entire section 3 or level1 for CIS Level 1 Profile Definitions) -f specify an AWS region to run checks against (i.e.: us-west-1) -m specify the maximum number of items to return for long-running requests (default: 100) From ac8179ec837755ade3b3935f5b4cddc50aeb6add Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 11:51:57 -0500 Subject: [PATCH 16/61] added extra76 check public AMIs --- README.md | 1 + prowler | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 76de37dd..acfe51bb 100644 --- a/README.md +++ b/README.md @@ -582,6 +582,7 @@ At this momment we have 5 extra checks: - 7.3 (`extra73`) Ensure there are no S3 buckets open to the Everyone or Any AWS user (Not Scored) (Not part of CIS benchmark) - 7.4 (`extra74`) Ensure there are no Security Groups without ingress filtering being used (Not Scored) (Not part of CIS benchmark) - 7.5 (`extra75`) Ensure there are no Security Groups not being used (Not Scored) (Not part of CIS benchmark) +- 7.6 (`extra76`) Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark) ``` ./prowler -c extras diff --git a/prowler b/prowler index e8e82b24..48a2cd09 100755 --- a/prowler +++ b/prowler @@ -1647,7 +1647,24 @@ extra75(){ fi done done +} +extra76(){ + #set -x + ID76="7.6,7.06" + TITLE76="Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID76" "$TITLE76" "NOT_SCORED" "EXTRA" + textNotice "Looking for AMIs in all regions... " + for regx in $REGIONS; do + LIST_OF_PUBLIC_AMIS=$($AWSCLI ec2 describe-images --owners self $PROFILE_OPT --region $regx --filters "Name=is-public,Values=true" --query 'Images[*].{ID:ImageId}' --output text) + if [[ $LIST_OF_PUBLIC_AMIS ]];then + for ami in $LIST_OF_PUBLIC_AMIS; do + textWarn "$regx: $ami is currently Public!" "$regx" + done + else + textOK "$regx: No Public AMIs found" "$regx" + fi + done } callCheck(){ @@ -1710,6 +1727,7 @@ callCheck(){ extra73|extra703 ) extra73;; extra74|extra704 ) extra74;; extra75|extra705 ) extra75;; + extra76|extra706 ) extra76;; ## Groups of Checks check1 ) @@ -1745,7 +1763,7 @@ callCheck(){ check43;check44;check45 ;; extras ) - extra71;extra72;extra73;extra74;extra75 + extra71;extra72;extra73;extra74;extra75;extra76 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -1841,6 +1859,7 @@ extra72 extra73 extra74 extra75 +extra76 cleanTemp exit $EXITCODE From bfdba6257b4c6194e8794674a9f1d9db95c23248 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 14:20:35 -0500 Subject: [PATCH 17/61] added -l flag to show only a list of all checks --- README.md | 1 + prowler | 386 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 256 insertions(+), 131 deletions(-) diff --git a/README.md b/README.md index acfe51bb..8a5970b4 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ USAGE: -M output mode: text (defalut), mono, csv (separator is ","; data is on stdout; progress on stderr) -k keep the credential report -n show check numbers to sort easier (i.e.: 1.01 instead of 1.1) + -l list all available checks only (does not perform any check) -h this help ``` diff --git a/prowler b/prowler index 48a2cd09..bb95f3ad 100755 --- a/prowler +++ b/prowler @@ -54,18 +54,22 @@ USAGE: -M output mode: text (default), mono, csv (separator is \"${SEP}\"; data is on stdout; progress on stderr) -k keep the credential report -n show check numbers to sort easier (i.e.: 1.01 instead of 1.1) + -l list all available checks only (does not perform any check) -h this help " exit } -while getopts ":hkp:r:c:f:m:M:n" OPTION; do +while getopts ":hlkp:r:c:f:m:M:n" OPTION; do case $OPTION in h ) usage EXITCODE=1 exit $EXITCODE ;; + l ) + PRINTCHECKSONLY=1 + ;; k ) KEEPCREDREPORT=1 ;; @@ -354,6 +358,129 @@ textTitle(){ fi } +# List of checks IDs and Titles +TITLE1="Identity and Access Management ****************************************" +ID11="1.1,1.01" +TITLE11="Avoid the use of the root account (Scored)." +ID12="1.2,1.02" +TITLE12="Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password (Scored)" +ID13="1.3,1.03" +TITLE13="Ensure credentials unused for 90 days or greater are disabled (Scored)" +ID14="1.4,1.04" +TITLE14="Ensure access keys are rotated every 90 days or less (Scored)" # also checked by Security Monkey +ID15="1.5,1.05" +TITLE15="Ensure IAM password policy requires at least one uppercase letter (Scored)" +ID16="1.6,1.06" +TITLE16="Ensure IAM password policy require at least one lowercase letter (Scored)" +ID17="1.7,1.07" +TITLE17="Ensure IAM password policy require at least one symbol (Scored)" +ID18="1.8,1.08" +TITLE18="Ensure IAM password policy require at least one number (Scored)" +ID19="1.9,1.09" +TITLE19="Ensure IAM password policy requires minimum length of 14 or greater (Scored)" +ID110="1.10" +TITLE110="Ensure IAM password policy prevents password reuse: 24 or greater (Scored)" +ID111="1.11" +TITLE111="Ensure IAM password policy expires passwords within 90 days or less (Scored)" +ID112="1.12" +TITLE112="Ensure no root account access key exists (Scored)" +ID113="1.13" +TITLE113="Ensure MFA is enabled for the root account (Scored)" +ID114="1.14" +TITLE114="Ensure hardware MFA is enabled for the root account (Scored)" +ID115="1.15" +TITLE115="Ensure security questions are registered in the AWS account (Not Scored)" +ID116="1.16" +TITLE116="Ensure IAM policies are attached only to groups or roles (Scored)" +ID117="1.17" +TITLE117="Enable detailed billing (Scored)" +ID118="1.18" +TITLE118="Ensure IAM Master and IAM Manager roles are active (Scored)" +ID119="1.19" +TITLE119="Maintain current contact details (Scored)" +ID120="1.20" +TITLE120="Ensure security contact information is registered (Scored)" +ID121="1.21" +TITLE121="Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)" +ID122="1.22" +TITLE122="Ensure a support role has been created to manage incidents with AWS Support (Scored)" +ID123="1.23" +TITLE123="Do not setup access keys during initial user setup for all IAM users that have a console password (Not Scored)" +ID124="1.24" +TITLE124="Ensure IAM policies that allow full \"*:*\" administrative privileges are not created (Scored)" +TITLE2="Logging ***************************************************************" +ID21="2.1,2.01" +TITLE21="Ensure CloudTrail is enabled in all regions (Scored)" +ID22="2.2,2.02" +TITLE22="Ensure CloudTrail log file validation is enabled (Scored)" +ID23="2.3,2.03" +TITLE23="Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)" +ID24="2.4,2.04" +TITLE24="Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)" +ID25="2.5,2.05" +TITLE25="Ensure AWS Config is enabled in all regions (Scored)" +ID26="2.6,2.06" +TITLE26="Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" +ID27="2.7,2.07" +TITLE27="Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)" +ID28="2.8,2.08" +TITLE28="Ensure rotation for customer created CMKs is enabled (Scored)" +TITLE3="Monitoring ************************************************************" +ID31="3.1,3.01" +TITLE31="Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" +ID32="3.2,3.02" +TITLE32="Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" +ID33="3.3,3.03" +TITLE33="Ensure a log metric filter and alarm exist for usage of root account (Scored)" +ID34="3.4,3.04" +TITLE34="Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" +ID35="3.5,3.05" +TITLE35="Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" +ID36="3.6,3.06" +TITLE36="Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" +ID37="3.7,3.07" +TITLE37="Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" +ID38="3.8,3.08" +TITLE38="Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" +ID39="3.9,3.09" +TITLE39="Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" +ID310="3.10" +TITLE310="Ensure a log metric filter and alarm exist for security group changes (Scored)" +ID311="3.11" +TITLE311="Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" +ID312="3.12" +TITLE312="Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" +ID313="3.13" +TITLE313="Ensure a log metric filter and alarm exist for route table changes (Scored)" +ID314="3.14" +TITLE314="Ensure a log metric filter and alarm exist for VPC changes (Scored)" +ID315="3.15" +TITLE315="Ensure appropriate subscribers to each SNS topic (Not Scored)" +TITLE4="Networking ************************************************************" +ID41="4.1,4.01" +TITLE41="Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored)" +ID42="4.2,4.02" +TITLE42="Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored)" +ID43="4.3,4.03" +TITLE43="Ensure VPC Flow Logging is Enabled in all VPCs (Scored)" +ID44="4.4,4.04" +TITLE44="Ensure the default security group of every VPC restricts all traffic (Scored)" +ID45="4.5,4.05" +TITLE45="Ensure routing tables for VPC peering are \"least access\" (Not Scored)" +TITLE7="Extras ****************************************************************" +ID71="7.1,7.01" +TITLE71="Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark)" +ID72="7.2,7.02" +TITLE72="Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark)" +ID73="7.3,7.03" +TITLE73="Ensure there are no S3 buckets open to the Everyone or Any AWS user (Not Scored) (Not part of CIS benchmark)" +ID74="7.4,7.04" +TITLE74="Ensure there are no Security Groups without ingress filtering being used (Not Scored) (Not part of CIS benchmark)" +ID75="7.5,7.05" +TITLE75="Ensure there are no Security Groups not being used (Not Scored) (Not part of CIS benchmark)" +ID76="7.6,7.06" +TITLE76="Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark)" + printCsvHeader() { >&2 echo "" >&2 echo "Generating \"${SEP}\" delimited report on stdout for profile $PROFILE, account $ACCOUNT_NUM" @@ -470,16 +597,14 @@ infoReferenceLong(){ } check11(){ - ID11="1.1,1.01" - TITLE11="Avoid the use of the root account (Scored)." + # "Avoid the use of the root account (Scored)." COMMAND11=$(cat $TEMP_REPORT_FILE| grep '' | cut -d, -f5,11,16 | sed 's/,/\ /g') textTitle "$ID11" "$TITLE11" "SCORED" "LEVEL1" textNotice "Root account last accessed (password key_1 key_2): $COMMAND11" } check12(){ - ID12="1.2,1.02" - TITLE12="Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password (Scored)" + # "Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password (Scored)" # List users with password enabled COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$4 }' |grep true | awk '{ print $1 }') COMMAND12=$( @@ -497,8 +622,7 @@ check12(){ } check13(){ - ID13="1.3,1.03" - TITLE13="Ensure credentials unused for 90 days or greater are disabled (Scored)" + # "Ensure credentials unused for 90 days or greater are disabled (Scored)" textTitle "$ID13" "$TITLE13" "SCORED" "LEVEL1" COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED=$(cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$4 }' |grep true | awk '{ print $1 }') if [[ $COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED ]]; then @@ -527,8 +651,7 @@ check13(){ } check14(){ - ID14="1.4,1.04" - TITLE14="Ensure access keys are rotated every 90 days or less (Scored)" # also checked by Security Monkey + # "Ensure access keys are rotated every 90 days or less (Scored)" # also checked by Security Monkey LIST_OF_USERS_WITH_ACCESS_KEY1=$(cat $TEMP_REPORT_FILE| awk -F, '{ print $1, $9 }' |grep "\ true" | awk '{ print $1 }') LIST_OF_USERS_WITH_ACCESS_KEY2=$(cat $TEMP_REPORT_FILE| awk -F, '{ print $1, $14 }' |grep "\ true" | awk '{ print $1 }') textTitle "$ID14" "$TITLE14" "SCORED" "LEVEL1" @@ -573,8 +696,7 @@ check14(){ } check15(){ - ID15="1.5,1.05" - TITLE15="Ensure IAM password policy requires at least one uppercase letter (Scored)" + # "Ensure IAM password policy requires at least one uppercase letter (Scored)" COMMAND15=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireUppercaseCharacters' 2> /dev/null) # must be true textTitle "$ID15" "$TITLE15" "SCORED" "LEVEL1" if [[ "$COMMAND15" == "true" ]];then @@ -585,8 +707,7 @@ check15(){ } check16(){ - ID16="1.6,1.06" - TITLE16="Ensure IAM password policy require at least one lowercase letter (Scored)" + # "Ensure IAM password policy require at least one lowercase letter (Scored)" COMMAND16=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireLowercaseCharacters' 2> /dev/null) # must be true textTitle "$ID16" "$TITLE16" "SCORED" "LEVEL1" if [[ "$COMMAND16" == "true" ]];then @@ -597,8 +718,7 @@ check16(){ } check17(){ - ID17="1.7,1.07" - TITLE17="Ensure IAM password policy require at least one symbol (Scored)" + # "Ensure IAM password policy require at least one symbol (Scored)" COMMAND17=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireSymbols' 2> /dev/null) # must be true textTitle "$ID17" "$TITLE17" "SCORED" "LEVEL1" if [[ "$COMMAND17" == "true" ]];then @@ -609,8 +729,7 @@ check17(){ } check18(){ - ID18="1.8,1.08" - TITLE18="Ensure IAM password policy require at least one number (Scored)" + # "Ensure IAM password policy require at least one number (Scored)" COMMAND18=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.RequireNumbers' 2> /dev/null) # must be true textTitle "$ID18" "$TITLE18" "SCORED" "LEVEL1" if [[ "$COMMAND18" == "true" ]];then @@ -621,8 +740,7 @@ check18(){ } check19(){ - ID19="1.9,1.09" - TITLE19="Ensure IAM password policy requires minimum length of 14 or greater (Scored)" + # "Ensure IAM password policy requires minimum length of 14 or greater (Scored)" COMMAND19=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json --query 'PasswordPolicy.MinimumPasswordLength' 2> /dev/null) textTitle "$ID19" "$TITLE19" "SCORED" "LEVEL1" if [[ $COMMAND19 -gt "13" ]];then @@ -633,8 +751,7 @@ check19(){ } check110(){ - ID110="1.10" - TITLE110="Ensure IAM password policy prevents password reuse: 24 or greater (Scored)" + # "Ensure IAM password policy prevents password reuse: 24 or greater (Scored)" COMMAND110=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --query 'PasswordPolicy.PasswordReusePrevention' --output text 2> /dev/null) textTitle "$ID110" "$TITLE110" "SCORED" "LEVEL1" if [[ $COMMAND110 ]];then @@ -649,8 +766,7 @@ check110(){ } check111(){ - ID111="1.11" - TITLE111="Ensure IAM password policy expires passwords within 90 days or less (Scored)" + # "Ensure IAM password policy expires passwords within 90 days or less (Scored)" COMMAND111=$($AWSCLI iam get-account-password-policy $PROFILE_OPT --region $REGION --output json | grep MaxPasswordAge | awk -F: '{ print $2 }'|sed 's/\ //g'|sed 's/,/ /g' 2> /dev/null) textTitle "$ID111" "$TITLE111" "SCORED" "LEVEL1" if [[ $COMMAND111 ]];then @@ -663,8 +779,7 @@ check111(){ } check112(){ - ID112="1.12" - TITLE112="Ensure no root account access key exists (Scored)" + # "Ensure no root account access key exists (Scored)" # ensure the access_key_1_active and access_key_2_active fields are set to FALSE. ROOTKEY1=$(cat $TEMP_REPORT_FILE |grep root_account|awk -F',' '{ print $9 }') ROOTKEY2=$(cat $TEMP_REPORT_FILE |grep root_account|awk -F',' '{ print $14 }') @@ -682,8 +797,7 @@ check112(){ } check113(){ - ID113="1.13" - TITLE113="Ensure MFA is enabled for the root account (Scored)" + # "Ensure MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled') textTitle "$ID113" "$TITLE113" "SCORED" "LEVEL1" if [ "$COMMAND113" == "1" ]; then @@ -694,8 +808,7 @@ check113(){ } check114(){ - ID114="1.14" - TITLE114="Ensure hardware MFA is enabled for the root account (Scored)" + # "Ensure hardware MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled') textTitle "$ID114" "$TITLE114" "SCORED" "LEVEL1" if [ "$COMMAND113" == "1" ]; then @@ -711,8 +824,7 @@ check114(){ } check115(){ - ID115="1.15" - TITLE115="Ensure security questions are registered in the AWS account (Not Scored)" + # "Ensure security questions are registered in the AWS account (Not Scored)" textTitle "$ID115" "$TITLE115" "NOT_SCORED" "LEVEL2" textNotice "No command available for check 1.15 " textNotice "Login to the AWS Console as root & click on the Account " @@ -720,8 +832,7 @@ check115(){ } check116(){ - ID116="1.16" - TITLE116="Ensure IAM policies are attached only to groups or roles (Scored)" + # "Ensure IAM policies are attached only to groups or roles (Scored)" textTitle "$ID116" "$TITLE116" "SCORED" "LEVEL1" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text $PROFILE_OPT --region $REGION) C116_NUM_USERS=0 @@ -738,8 +849,7 @@ check116(){ } check117(){ - ID117="1.17" - TITLE117="Enable detailed billing (Scored)" + # "Enable detailed billing (Scored)" # No command available textTitle "$ID117" "$TITLE117" "SCORED" "LEVEL1" textNotice "No command available for check 1.17 " @@ -747,8 +857,7 @@ check117(){ } check118(){ - ID118="1.18" - TITLE118="Ensure IAM Master and IAM Manager roles are active (Scored)" + # "Ensure IAM Master and IAM Manager roles are active (Scored)" textTitle "$ID118" "$TITLE118" "SCORED" "LEVEL1" FINDMASTERANDMANAGER=$($AWSCLI iam list-roles $PROFILE_OPT --region $REGION --query "Roles[*].{RoleName:RoleName}" --output text | grep -E 'Master|Manager'| tr '\n' ' ') if [[ $FINDMASTERANDMANAGER ]];then @@ -773,8 +882,7 @@ check118(){ } check119(){ - ID119="1.19" - TITLE119="Maintain current contact details (Scored)" + # "Maintain current contact details (Scored)" # No command available textTitle "$ID119" "$TITLE119" "SCORED" "LEVEL1" textNotice "No command available for check 1.19 " @@ -782,8 +890,7 @@ check119(){ } check120(){ - ID120="1.20" - TITLE120="Ensure security contact information is registered (Scored)" + # "Ensure security contact information is registered (Scored)" # No command available textTitle "$ID120" "$TITLE120" "SCORED" "LEVEL1" textNotice "No command available for check 1.20 " @@ -791,16 +898,14 @@ check120(){ } check121(){ - ID121="1.21" - TITLE121="Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)" + # "Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)" textTitle "$ID121" "$TITLE121" "NOT_SCORED" "LEVEL2" textNotice "No command available for check 1.21 " textNotice "See section 1.21 on the CIS Benchmark guide for details " } check122(){ - ID122="1.22" - TITLE122="Ensure a support role has been created to manage incidents with AWS Support (Scored)" + # "Ensure a support role has been created to manage incidents with AWS Support (Scored)" textTitle "$ID122" "$TITLE122" "SCORED" "LEVEL1" SUPPORTPOLICYARN=$($AWSCLI iam list-policies --query "Policies[?PolicyName == 'AWSSupportAccess'].Arn" $PROFILE_OPT --region $REGION --output text) if [[ $SUPPORTPOLICYARN ]];then @@ -822,8 +927,7 @@ check122(){ } check123(){ - ID123="1.23" - TITLE123="Do not setup access keys during initial user setup for all IAM users that have a console password (Not Scored)" + # "Do not setup access keys during initial user setup for all IAM users that have a console password (Not Scored)" textTitle "$ID123" "$TITLE123" "NOT_SCORED" "LEVEL1" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text $PROFILE_OPT --region $REGION) # List of USERS with KEY1 last_used_date as N/A @@ -849,8 +953,7 @@ check123(){ } check124(){ - ID124="1.24" - TITLE124="Ensure IAM policies that allow full \"*:*\" administrative privileges are not created (Scored)" + # "Ensure IAM policies that allow full \"*:*\" administrative privileges are not created (Scored)" textTitle "$ID124" "$TITLE124" "SCORED" "LEVEL1" LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text $PROFILE_OPT --region $REGION|grep 'arn:aws:iam::[0-9]\{12\}:'|awk '{ print $2 }') if [[ $LIST_CUSTOM_POLICIES ]]; then @@ -876,8 +979,7 @@ check124(){ } check21(){ - ID21="2.1,2.01" - TITLE21="Ensure CloudTrail is enabled in all regions (Scored)" + # "Ensure CloudTrail is enabled in all regions (Scored)" textTitle "$ID21" "$TITLE21" "SCORED" "LEVEL1" LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].Name' --output text) if [[ $LIST_OF_TRAILS ]];then @@ -895,8 +997,7 @@ check21(){ } check22(){ - ID22="2.2,2.02" - TITLE22="Ensure CloudTrail log file validation is enabled (Scored)" + # "Ensure CloudTrail log file validation is enabled (Scored)" textTitle "$ID22" "$TITLE22" "SCORED" "LEVEL2" LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].Name' --output text) if [[ $LIST_OF_TRAILS ]];then @@ -914,8 +1015,7 @@ check22(){ } check23(){ - ID23="2.3,2.03" - TITLE23="Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)" + # "Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)" textTitle "$ID23" "$TITLE23" "SCORED" "LEVEL1" CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text $PROFILE_OPT --region $REGION) if [[ $CLOUDTRAILBUCKET ]];then @@ -933,8 +1033,7 @@ check23(){ } check24(){ - ID24="2.4,2.04" - TITLE24="Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)" + # "Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)" textTitle "$ID24" "$TITLE24" "SCORED" "LEVEL1" TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion}' --output text | tr "\t" ',') if [[ $TRAILS_AND_REGIONS ]];then @@ -960,8 +1059,7 @@ check24(){ } check25(){ - ID25="2.5,2.05" - TITLE25="Ensure AWS Config is enabled in all regions (Scored)" + # "Ensure AWS Config is enabled in all regions (Scored)" textTitle "$ID25" "$TITLE25" "SCORED" "LEVEL1" for regx in $REGIONS; do CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status $PROFILE_OPT --region $regx --output json| grep "recorder: ON") @@ -974,8 +1072,7 @@ check25(){ } check26(){ - ID26="2.6,2.06" - TITLE26="Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" + # "Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" textTitle "$ID26" "$TITLE26" "SCORED" "LEVEL1" CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text $PROFILE_OPT --region $REGION) if [[ $CLOUDTRAILBUCKET ]];then @@ -993,8 +1090,7 @@ check26(){ } check27(){ - ID27="2.7,2.07" - TITLE27="Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)" + # "Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)" textTitle "$ID27" "$TITLE27" "SCORED" "LEVEL2" CLOUDTRAILNAME=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].Name' --output text $PROFILE_OPT --region $REGION) if [[ $CLOUDTRAILNAME ]];then @@ -1012,8 +1108,7 @@ check27(){ } check28(){ - ID28="2.8,2.08" - TITLE28="Ensure rotation for customer created CMKs is enabled (Scored)" + # "Ensure rotation for customer created CMKs is enabled (Scored)" textTitle "$ID28" "$TITLE28" "SCORED" "LEVEL2" for regx in $REGIONS; do CHECK_KMS_KEYLIST=$($AWSCLI kms list-keys $PROFILE_OPT --region $regx --output text --query 'Keys[*].KeyId') @@ -1043,8 +1138,7 @@ check28(){ } check31(){ - ID31="3.1,3.01" - TITLE31="Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" + # "Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" textTitle "$ID31" "$TITLE31" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text| tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1068,8 +1162,7 @@ check31(){ } check32(){ - ID32="3.2,3.02" - TITLE32="Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" + # "Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" textTitle "$ID32" "$TITLE32" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1093,8 +1186,7 @@ check32(){ } check33(){ - ID33="3.3,3.03" - TITLE33="Ensure a log metric filter and alarm exist for usage of root account (Scored)" + # "Ensure a log metric filter and alarm exist for usage of root account (Scored)" textTitle "$ID33" "$TITLE33" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1118,8 +1210,7 @@ check33(){ } check34(){ - ID34="3.4,3.04" - TITLE34="Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" + # "Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" textTitle "$ID34" "$TITLE34" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1143,8 +1234,7 @@ check34(){ } check35(){ - ID35="3.5,3.05" - TITLE35="Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" + # "Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" textTitle "$ID35" "$TITLE35" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1168,8 +1258,7 @@ check35(){ } check36(){ - ID36="3.6,3.06" - TITLE36="Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" + # "Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" textTitle "$ID36" "$TITLE36" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1193,8 +1282,7 @@ check36(){ } check37(){ - ID37="3.7,3.07" - TITLE37="Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" + # "Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" textTitle "$ID37" "$TITLE37" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1218,8 +1306,7 @@ check37(){ } check38(){ - ID38="3.8,3.08" - TITLE38="Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" + # "Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" textTitle "$ID38" "$TITLE38" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1243,8 +1330,7 @@ check38(){ } check39(){ - ID39="3.9,3.09" - TITLE39="Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" + # "Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" textTitle "$ID39" "$TITLE39" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1268,8 +1354,7 @@ check39(){ } check310(){ - ID310="3.10" - TITLE310="Ensure a log metric filter and alarm exist for security group changes (Scored)" + # "Ensure a log metric filter and alarm exist for security group changes (Scored)" textTitle "$ID310" "$TITLE310" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1293,8 +1378,7 @@ check310(){ } check311(){ - ID311="3.11" - TITLE311="Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" + # "Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" textTitle "$ID311" "$TITLE311" "SCORED" "LEVEL2" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1318,8 +1402,7 @@ check311(){ } check312(){ - ID312="3.12" - TITLE312="Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" + # "Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" textTitle "$ID312" "$TITLE312" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1343,8 +1426,7 @@ check312(){ } check313(){ - ID313="3.13" - TITLE313="Ensure a log metric filter and alarm exist for route table changes (Scored)" + # "Ensure a log metric filter and alarm exist for route table changes (Scored)" textTitle "$ID313" "$TITLE313" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1368,8 +1450,7 @@ check313(){ } check314(){ - ID314="3.14" - TITLE314="Ensure a log metric filter and alarm exist for VPC changes (Scored)" + # "Ensure a log metric filter and alarm exist for VPC changes (Scored)" textTitle "$ID314" "$TITLE314" "SCORED" "LEVEL1" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | awk -F: '{ print $7 }') if [[ $CLOUDWATCH_GROUP ]];then @@ -1393,8 +1474,7 @@ check314(){ } check315(){ - ID315="3.15" - TITLE315="Ensure appropriate subscribers to each SNS topic (Not Scored)" + # "Ensure appropriate subscribers to each SNS topic (Not Scored)" textTitle "$ID315" "$TITLE315" "NOT_SCORED" "LEVEL1" CAN_SNS_LIST_SUBS=1 for regx in $REGIONS; do @@ -1430,8 +1510,7 @@ check315(){ } check41(){ - ID41="4.1,4.01" - TITLE41="Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored)" + # "Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored)" textTitle "$ID41" "$TITLE41" "SCORED" "LEVEL1" for regx in $REGIONS; do SG_LIST=$($AWSCLI ec2 describe-security-groups --filters "Name=ip-permission.to-port,Values=22" --query 'SecurityGroups[?length(IpPermissions[?ToPort==`22` && contains(IpRanges[].CidrIp, `0.0.0.0/0`)]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text) @@ -1446,8 +1525,7 @@ check41(){ } check42(){ - ID42="4.2,4.02" - TITLE42="Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored)" + # "Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored)" textTitle "$ID42" "$TITLE42" "SCORED" "LEVEL1" for regx in $REGIONS; do SG_LIST=$($AWSCLI ec2 describe-security-groups --filters "Name=ip-permission.to-port,Values=3389" --query 'SecurityGroups[?length(IpPermissions[?ToPort==`3389` && contains(IpRanges[].CidrIp, `0.0.0.0/0`)]) > `0`].{GroupName: GroupName}' $PROFILE_OPT --region $regx --output text) @@ -1462,8 +1540,7 @@ check42(){ } check43(){ - ID43="4.3,4.03" - TITLE43="Ensure VPC Flow Logging is Enabled in all VPCs (Scored)" + # "Ensure VPC Flow Logging is Enabled in all VPCs (Scored)" textTitle "$ID43" "$TITLE43" "SCORED" "LEVEL2" for regx in $REGIONS; do CHECK_FL=$($AWSCLI ec2 describe-flow-logs $PROFILE_OPT --region $regx --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].LogGroupName' --output text) @@ -1478,8 +1555,7 @@ check43(){ } check44(){ - ID44="4.4,4.04" - TITLE44="Ensure the default security group of every VPC restricts all traffic (Scored)" + # "Ensure the default security group of every VPC restricts all traffic (Scored)" textTitle "$ID44" "$TITLE44" "SCORED" "LEVEL2" for regx in $REGIONS; do CHECK_SGDEFAULT=$($AWSCLI ec2 describe-security-groups $PROFILE_OPT --region $regx --filters Name=group-name,Values='default' --query 'SecurityGroups[*].{IpPermissions:IpPermissions,IpPermissionsEgress:IpPermissionsEgress,GroupId:GroupId}' --output text |grep 0.0.0.0) @@ -1492,9 +1568,7 @@ check44(){ } check45(){ - #set -xe - ID45="4.5,4.05" - TITLE45="Ensure routing tables for VPC peering are \"least access\" (Not Scored)" + # "Ensure routing tables for VPC peering are \"least access\" (Not Scored)" textTitle "$ID45" "$TITLE45" "NOT_SCORED" "LEVEL2" textNotice "Looking for VPC peering in all regions... " for regx in $REGIONS; do @@ -1514,9 +1588,7 @@ check45(){ } extra71(){ - # set -x - ID71="7.1,7.01" - TITLE71="Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark)" + # "Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark)" textTitle "$ID71" "$TITLE71" "NOT_SCORED" "EXTRA" ADMIN_GROUPS='' @@ -1548,9 +1620,7 @@ extra71(){ } extra72(){ - #set -x - ID72="7.2,7.02" - TITLE72="Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark)" + # "Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark)" textTitle "$ID72" "$TITLE72" "NOT_SCORED" "EXTRA" textNotice "Looking for EBS Snapshots in all regions... " for regx in $REGIONS; do @@ -1568,9 +1638,7 @@ extra72(){ } extra73(){ - #set -x - ID73="7.3,7.03" - TITLE73="Ensure there are no S3 buckets open to the Everyone or Any AWS user (Not Scored) (Not part of CIS benchmark)" + # "Ensure there are no S3 buckets open to the Everyone or Any AWS user (Not Scored) (Not part of CIS benchmark)" textTitle "$ID73" "$TITLE73" "NOT_SCORED" "EXTRA" textNotice "Looking for open S3 Buckets (ACLs and Policies) in all regions... " ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' $PROFILE_OPT --region $REGION --output text) @@ -1611,9 +1679,7 @@ extra73(){ } extra74(){ - #set -x - ID74="7.4,7.04" - TITLE74="Ensure there are no Security Groups without ingress filtering being used (Not Scored) (Not part of CIS benchmark)" + # "Ensure there are no Security Groups without ingress filtering being used (Not Scored) (Not part of CIS benchmark)" textTitle "$ID74" "$TITLE74" "NOT_SCORED" "EXTRA" textNotice "Looking for Security Groups in all regions... " for regx in $REGIONS; do @@ -1631,9 +1697,7 @@ extra74(){ } extra75(){ - #set -x - ID75="7.5,7.05" - TITLE75="Ensure there are no Security Groups not being used (Not Scored) (Not part of CIS benchmark)" + # "Ensure there are no Security Groups not being used (Not Scored) (Not part of CIS benchmark)" textTitle "$ID75" "$TITLE75" "NOT_SCORED" "EXTRA" textNotice "Looking for Security Groups in all regions... " for regx in $REGIONS; do @@ -1650,9 +1714,7 @@ extra75(){ } extra76(){ - #set -x - ID76="7.6,7.06" - TITLE76="Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark)" + # "Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark)" textTitle "$ID76" "$TITLE76" "NOT_SCORED" "EXTRA" textNotice "Looking for AMIs in all regions... " for regx in $REGIONS; do @@ -1773,6 +1835,75 @@ callCheck(){ fi } +# List only check tittles + +if [[ $PRINTCHECKSONLY == "1" ]]; then + prowlerBanner + textTitle "1" "$TITLE1" "NOT_SCORED" "SUPPORT" + textTitle "$ID11" "$TITLE11" "SCORED" "LEVEL1" + textTitle "$ID12" "$TITLE12" "SCORED" "LEVEL1" + textTitle "$ID13" "$TITLE13" "SCORED" "LEVEL1" + textTitle "$ID14" "$TITLE14" "SCORED" "LEVEL1" + textTitle "$ID15" "$TITLE15" "SCORED" "LEVEL1" + textTitle "$ID16" "$TITLE16" "SCORED" "LEVEL1" + textTitle "$ID17" "$TITLE17" "SCORED" "LEVEL1" + textTitle "$ID18" "$TITLE18" "SCORED" "LEVEL1" + textTitle "$ID19" "$TITLE19" "SCORED" "LEVEL1" + textTitle "$ID110" "$TITLE110" "SCORED" "LEVEL1" + textTitle "$ID111" "$TITLE111" "SCORED" "LEVEL1" + textTitle "$ID112" "$TITLE112" "SCORED" "LEVEL1" + textTitle "$ID113" "$TITLE113" "SCORED" "LEVEL1" + textTitle "$ID114" "$TITLE114" "SCORED" "LEVEL1" + textTitle "$ID115" "$TITLE115" "NOT_SCORED" "LEVEL2" + textTitle "$ID116" "$TITLE116" "SCORED" "LEVEL1" + textTitle "$ID117" "$TITLE117" "SCORED" "LEVEL1" + textTitle "$ID118" "$TITLE118" "SCORED" "LEVEL1" + textTitle "$ID119" "$TITLE119" "SCORED" "LEVEL1" + textTitle "$ID120" "$TITLE120" "SCORED" "LEVEL1" + textTitle "$ID121" "$TITLE121" "NOT_SCORED" "LEVEL2" + textTitle "$ID122" "$TITLE122" "SCORED" "LEVEL1" + textTitle "$ID123" "$TITLE123" "NOT_SCORED" "LEVEL1" + textTitle "$ID124" "$TITLE124" "SCORED" "LEVEL1" + textTitle "2" "$TITLE2" "NOT_SCORED" "SUPPORT" + textTitle "$ID21" "$TITLE21" "SCORED" "LEVEL1" + textTitle "$ID22" "$TITLE22" "SCORED" "LEVEL2" + textTitle "$ID23" "$TITLE23" "SCORED" "LEVEL1" + textTitle "$ID24" "$TITLE24" "SCORED" "LEVEL1" + textTitle "$ID25" "$TITLE25" "SCORED" "LEVEL1" + textTitle "$ID26" "$TITLE26" "SCORED" "LEVEL1" + textTitle "$ID27" "$TITLE27" "SCORED" "LEVEL2" + textTitle "$ID28" "$TITLE28" "SCORED" "LEVEL2" + textTitle "3" "$TITLE3" "NOT_SCORED" "SUPPORT" + textTitle "$ID31" "$TITLE31" "SCORED" "LEVEL1" + textTitle "$ID32" "$TITLE32" "SCORED" "LEVEL1" + textTitle "$ID33" "$TITLE33" "SCORED" "LEVEL1" + textTitle "$ID34" "$TITLE34" "SCORED" "LEVEL1" + textTitle "$ID35" "$TITLE35" "SCORED" "LEVEL1" + textTitle "$ID36" "$TITLE36" "SCORED" "LEVEL2" + textTitle "$ID37" "$TITLE37" "SCORED" "LEVEL2" + textTitle "$ID38" "$TITLE38" "SCORED" "LEVEL1" + textTitle "$ID39" "$TITLE39" "SCORED" "LEVEL2" + textTitle "$ID310" "$TITLE310" "SCORED" "LEVEL2" + textTitle "$ID311" "$TITLE311" "SCORED" "LEVEL2" + textTitle "$ID312" "$TITLE312" "SCORED" "LEVEL1" + textTitle "$ID313" "$TITLE313" "SCORED" "LEVEL1" + textTitle "$ID314" "$TITLE314" "SCORED" "LEVEL1" + textTitle "$ID315" "$TITLE315" "NOT_SCORED" "LEVEL1" + textTitle "4" "$TITLE4" "NOT_SCORED" "SUPPORT" + textTitle "$ID41" "$TITLE41" "SCORED" "LEVEL1" + textTitle "$ID42" "$TITLE42" "SCORED" "LEVEL1" + textTitle "$ID43" "$TITLE43" "SCORED" "LEVEL2" + textTitle "$ID44" "$TITLE44" "SCORED" "LEVEL2" + textTitle "$ID45" "$TITLE45" "NOT_SCORED" "LEVEL2" + textTitle "7" "$TITLE7" "NOT_SCORED" "SUPPORT" + textTitle "$ID71" "$TITLE71" "NOT_SCORED" "EXTRA" + textTitle "$ID72" "$TITLE72" "NOT_SCORED" "EXTRA" + textTitle "$ID73" "$TITLE73" "NOT_SCORED" "EXTRA" + textTitle "$ID74" "$TITLE74" "NOT_SCORED" "EXTRA" + textTitle "$ID75" "$TITLE75" "NOT_SCORED" "EXTRA" + textTitle "$ID76" "$TITLE76" "NOT_SCORED" "EXTRA" + exit $EXITCODE +fi ### All functions defined above ... run the workflow @@ -1783,11 +1914,8 @@ fi getWhoami genCredReport saveReport - - callCheck -TITLE1="Identity and Access Management ****************************************" textTitle "1" "$TITLE1" "NOT_SCORED" "SUPPORT" check11 check12 @@ -1814,7 +1942,6 @@ check122 check123 check124 -TITLE2="Logging ***************************************************************" textTitle "2" "$TITLE2" "NOT_SCORED" "SUPPORT" check21 check22 @@ -1825,7 +1952,6 @@ check26 check27 check28 -TITLE3="Monitoring ************************************************************" textTitle "3" "$TITLE3" "NOT_SCORED" "SUPPORT" # 3 Monitoring check commands / Mostly covered by SecurityMonkey check31 @@ -1844,7 +1970,6 @@ check313 check314 check315 -TITLE4="Networking ************************************************************" textTitle "4" "$TITLE4" "NOT_SCORED" "SUPPORT" check41 check42 @@ -1852,7 +1977,6 @@ check43 check44 check45 -TITLE7="Extras ************************************************************" textTitle "7" "$TITLE7" "NOT_SCORED" "SUPPORT" extra71 extra72 From e5c65fd5233f30f57a9cc84f61515084d752d0c2 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 14:59:23 -0500 Subject: [PATCH 18/61] added extra77 check public ECR repos --- README.md | 1 + prowler | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a5970b4..ee60e09a 100644 --- a/README.md +++ b/README.md @@ -584,6 +584,7 @@ At this momment we have 5 extra checks: - 7.4 (`extra74`) Ensure there are no Security Groups without ingress filtering being used (Not Scored) (Not part of CIS benchmark) - 7.5 (`extra75`) Ensure there are no Security Groups not being used (Not Scored) (Not part of CIS benchmark) - 7.6 (`extra76`) Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark) +- 7.7 (`extra77`) Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark) ``` ./prowler -c extras diff --git a/prowler b/prowler index bb95f3ad..d09bdcf0 100755 --- a/prowler +++ b/prowler @@ -480,6 +480,8 @@ ID75="7.5,7.05" TITLE75="Ensure there are no Security Groups not being used (Not Scored) (Not part of CIS benchmark)" ID76="7.6,7.06" TITLE76="Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark)" +ID77="7.7,7.07" +TITLE77="Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { >&2 echo "" @@ -1729,6 +1731,28 @@ extra76(){ done } +extra77(){ + # "Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID77" "$TITLE77" "NOT_SCORED" "EXTRA" + textNotice "Looking for ECR repos in all regions... " + for regx in $REGIONS; do + LIST_OF_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query 'repositories[*].{Name:repositoryName}' --output text) + for ecr_repo in $LIST_OF_ECR_REPOS; do + TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-ecr-repo.policy.XXXXXXXXXX) + $AWSCLI ecr get-repository-policy --repository-name $ecr_repo $PROFILE_OPT --region $regx --output text > $TEMP_POLICY_FILE 2> /dev/null + # check if the policy has Principal as * + CHECK_ECR_REPO_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | awk '/Principal/ && !skip { print } { skip = /Deny/} '|grep \"Principal|grep \*) + if [[ $CHECK_ECR_REPO_ALLUSERS_POLICY ]];then + textWarn "$regx: $ecr_repo policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx" + else + textOK "$regx: $ecr_repo is not open" "$regx" + fi + done + rm -fr $TEMP_POLICY_FILE + done + +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -1790,6 +1814,7 @@ callCheck(){ extra74|extra704 ) extra74;; extra75|extra705 ) extra75;; extra76|extra706 ) extra76;; + extra77|extra707 ) extra77;; ## Groups of Checks check1 ) @@ -1825,7 +1850,7 @@ callCheck(){ check43;check44;check45 ;; extras ) - extra71;extra72;extra73;extra74;extra75;extra76 + extra71;extra72;extra73;extra74;extra75;extra76;extra77 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -1902,6 +1927,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID74" "$TITLE74" "NOT_SCORED" "EXTRA" textTitle "$ID75" "$TITLE75" "NOT_SCORED" "EXTRA" textTitle "$ID76" "$TITLE76" "NOT_SCORED" "EXTRA" + textTitle "$ID77" "$TITLE77" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -1984,6 +2010,7 @@ extra73 extra74 extra75 extra76 +extra77 cleanTemp exit $EXITCODE From dc6b1dd915bdb07c6e0516db5bd236af8830a6f1 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 15:13:12 -0500 Subject: [PATCH 19/61] Added how to add Custom Checks --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index ee60e09a..bb623fbf 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ - [Screenshots](#screenshots) - [Troubleshooting](#troubleshooting) - [Extras](#extras) +- [Add custom checks](#custom checks) ## Description @@ -593,3 +594,12 @@ or to run just one of the checks, to see if you have S3 buckets open: ``` ./prowler -c extraNUMBER ``` + +## Custom Checks + +In order to add any new check feel free to create a new extra check in the extras section. +To do so, you will need to follow this steps: +1 - use any existing extra check as reference +2 - add `ID7N` and `TITLE7N`, where N is a new check number part of the extras section (7) around line 361 `# List of checks IDs and Titles` +3 - add your new extra check function name at `callCheck` function (around line 1817) and below in that case inside extras option (around line 1853) +4 - finally add it in `# List only check tittles` around line 1930 From 741ff97b026c6e8b20ff7bdeb38e0db7a70bed66 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 15:15:16 -0500 Subject: [PATCH 20/61] Added how to add Custom Checks to README --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bb623fbf..cc2ad7df 100644 --- a/README.md +++ b/README.md @@ -599,7 +599,9 @@ or to run just one of the checks, to see if you have S3 buckets open: In order to add any new check feel free to create a new extra check in the extras section. To do so, you will need to follow this steps: -1 - use any existing extra check as reference -2 - add `ID7N` and `TITLE7N`, where N is a new check number part of the extras section (7) around line 361 `# List of checks IDs and Titles` -3 - add your new extra check function name at `callCheck` function (around line 1817) and below in that case inside extras option (around line 1853) -4 - finally add it in `# List only check tittles` around line 1930 +#1 - use any existing extra check as reference +#2 - add `ID7N` and `TITLE7N`, where N is a new check number part of the extras section (7) around line 361 `# List of checks IDs and Titles` +#3 - add your new extra check function name at `callCheck` function (around line 1817) and below in that case inside extras option (around line 1853) +#4 - finally add it in `# List only check tittles` around line 1930 +#5 - save changes and run it as ./prowler -c extraNN +#6 - send me a pull request! :) From e04d2df95316a59df63344c527de46cdab70aa45 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 15:16:54 -0500 Subject: [PATCH 21/61] Added how to add Custom Checks to README --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cc2ad7df..a27cfbd6 100644 --- a/README.md +++ b/README.md @@ -599,9 +599,9 @@ or to run just one of the checks, to see if you have S3 buckets open: In order to add any new check feel free to create a new extra check in the extras section. To do so, you will need to follow this steps: -#1 - use any existing extra check as reference -#2 - add `ID7N` and `TITLE7N`, where N is a new check number part of the extras section (7) around line 361 `# List of checks IDs and Titles` -#3 - add your new extra check function name at `callCheck` function (around line 1817) and below in that case inside extras option (around line 1853) -#4 - finally add it in `# List only check tittles` around line 1930 -#5 - save changes and run it as ./prowler -c extraNN -#6 - send me a pull request! :) +- 1 - use any existing extra check as reference +- 2 - add `ID7N` and `TITLE7N`, where N is a new check number part of the extras section (7) around line 361 `# List of checks IDs and Titles` +- 3 - add your new extra check function name at `callCheck` function (around line 1817) and below in that case inside extras option (around line 1853) +- 4 - finally add it in `# List only check tittles` around line 1930 +- 5 - save changes and run it as ./prowler -c extraNN +- 6 - send me a pull request! :) From 993a58a0c4c4617e68032d876067318c2b1e2c1f Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 15:17:51 -0500 Subject: [PATCH 22/61] Added how to add Custom Checks to README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a27cfbd6..27a09d8f 100644 --- a/README.md +++ b/README.md @@ -595,10 +595,10 @@ or to run just one of the checks, to see if you have S3 buckets open: ./prowler -c extraNUMBER ``` -## Custom Checks +## Add Custom Checks + +In order to add any new check feel free to create a new extra check in the extras section. To do so, you will need to follow these steps: -In order to add any new check feel free to create a new extra check in the extras section. -To do so, you will need to follow this steps: - 1 - use any existing extra check as reference - 2 - add `ID7N` and `TITLE7N`, where N is a new check number part of the extras section (7) around line 361 `# List of checks IDs and Titles` - 3 - add your new extra check function name at `callCheck` function (around line 1817) and below in that case inside extras option (around line 1853) From d9685149d0945bc25b59f8577eb966234567677f Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 15:18:37 -0500 Subject: [PATCH 23/61] Added how to add Custom Checks to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27a09d8f..3547d5d8 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - [Screenshots](#screenshots) - [Troubleshooting](#troubleshooting) - [Extras](#extras) -- [Add custom checks](#custom checks) +- [Add Custom Checks](#add-custom-checks) ## Description From e6d858cbe38ce10f6287b168783d86dc612204e3 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 15:19:50 -0500 Subject: [PATCH 24/61] Added how to add Custom Checks to README --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3547d5d8..e515132e 100644 --- a/README.md +++ b/README.md @@ -599,9 +599,9 @@ or to run just one of the checks, to see if you have S3 buckets open: In order to add any new check feel free to create a new extra check in the extras section. To do so, you will need to follow these steps: -- 1 - use any existing extra check as reference -- 2 - add `ID7N` and `TITLE7N`, where N is a new check number part of the extras section (7) around line 361 `# List of checks IDs and Titles` -- 3 - add your new extra check function name at `callCheck` function (around line 1817) and below in that case inside extras option (around line 1853) -- 4 - finally add it in `# List only check tittles` around line 1930 -- 5 - save changes and run it as ./prowler -c extraNN -- 6 - send me a pull request! :) +1. use any existing extra check as reference +2. add `ID7N` and `TITLE7N`, where N is a new check number part of the extras section (7) around line 361 `# List of checks IDs and Titles` +3. add your new extra check function name at `callCheck` function (around line 1817) and below in that case inside extras option (around line 1853) +4. finally add it in `# List only check tittles` around line 1930 +5. save changes and run it as ./prowler -c extraNN +6. send me a pull request! :) From d004c28436e8016fc953af61d53bef4fe7469542 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Tue, 9 Jan 2018 15:25:48 -0500 Subject: [PATCH 25/61] Added Third Party Integrations to README --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index e515132e..d7702523 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ - [Troubleshooting](#troubleshooting) - [Extras](#extras) - [Add Custom Checks](#add-custom-checks) +- [Third Party Integrations](#third-party-integrations) ## Description @@ -605,3 +606,10 @@ In order to add any new check feel free to create a new extra check in the extra 4. finally add it in `# List only check tittles` around line 1930 5. save changes and run it as ./prowler -c extraNN 6. send me a pull request! :) + +## Third Party Integrations + +### Telegram +Javier Pecete has done an awesome job integrating Prowler with Telegram, you have more details here https://github.com/i4specete/ServerTelegramBot +### Cloud Security Suite +The guys of SecurityFTW have added Prowler in their Cloud Security Suite along with other cool security tools https://github.com/SecurityFTW/cs-suite From 36e851b79f932758a66f99c66628088d4a7d5c6e Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Thu, 11 Jan 2018 16:46:08 -0500 Subject: [PATCH 26/61] Added check extra78 --- README.md | 5 +++-- prowler | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d7702523..771a24b4 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ It covers hardening and security best practices for all AWS regions related to: - Logging (8 checks) - Monitoring (15 checks) - Networking (5 checks) -- Extra checks (5 checks) *see Extras section +- Extras (8 checks) *see Extras section For a comprehesive list and resolution look at the guide on the link above. @@ -587,6 +587,7 @@ At this momment we have 5 extra checks: - 7.5 (`extra75`) Ensure there are no Security Groups not being used (Not Scored) (Not part of CIS benchmark) - 7.6 (`extra76`) Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark) - 7.7 (`extra77`) Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark) +- 7.8 (`extra78`) Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark) ``` ./prowler -c extras @@ -612,4 +613,4 @@ In order to add any new check feel free to create a new extra check in the extra ### Telegram Javier Pecete has done an awesome job integrating Prowler with Telegram, you have more details here https://github.com/i4specete/ServerTelegramBot ### Cloud Security Suite -The guys of SecurityFTW have added Prowler in their Cloud Security Suite along with other cool security tools https://github.com/SecurityFTW/cs-suite +The guys of SecurityFTW have added Prowler in their Cloud Security Suite along with other cool security tools https://github.com/SecurityFTW/cs-suite diff --git a/prowler b/prowler index d09bdcf0..cd251b02 100755 --- a/prowler +++ b/prowler @@ -482,6 +482,9 @@ ID76="7.6,7.06" TITLE76="Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark)" ID77="7.7,7.07" TITLE77="Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark)" +ID78="7.8,7.08" +TITLE78="Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark)" + printCsvHeader() { >&2 echo "" @@ -1750,7 +1753,22 @@ extra77(){ done rm -fr $TEMP_POLICY_FILE done +} +extra78(){ + # "Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID78" "$TITLE78" "NOT_SCORED" "EXTRA" + textNotice "Looking for RDS instances in all regions... " + for regx in $REGIONS; do + LIST_OF_RDS_PUBLIC_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[?PubliclyAccessible==`true`].{id:DBInstanceIdentifier}' --output text) + if [[ $LIST_OF_RDS_PUBLIC_INSTANCES ]];then + for rds_instance in $(echo $LIST_OF_RDS_PUBLIC_INSTANCES);do + textWarn "$regx: RDS instance $rds_instance is set as Publicly Accessible!" "$regx" + done + else + textOK "$regx: no Publicly Accessible RDS instances found" "$regx" + fi + done } callCheck(){ @@ -1815,6 +1833,7 @@ callCheck(){ extra75|extra705 ) extra75;; extra76|extra706 ) extra76;; extra77|extra707 ) extra77;; + extra78|extra708 ) extra78;; ## Groups of Checks check1 ) @@ -1850,7 +1869,7 @@ callCheck(){ check43;check44;check45 ;; extras ) - extra71;extra72;extra73;extra74;extra75;extra76;extra77 + extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -1928,6 +1947,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID75" "$TITLE75" "NOT_SCORED" "EXTRA" textTitle "$ID76" "$TITLE76" "NOT_SCORED" "EXTRA" textTitle "$ID77" "$TITLE77" "NOT_SCORED" "EXTRA" + textTitle "$ID78" "$TITLE78" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2011,6 +2031,7 @@ extra74 extra75 extra76 extra77 +extra78 cleanTemp exit $EXITCODE From 609b4810f2312ba0688bde5758b6160d9e57e9b0 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Thu, 11 Jan 2018 16:47:58 -0500 Subject: [PATCH 27/61] Added check extra78 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 771a24b4..5112fb40 100644 --- a/README.md +++ b/README.md @@ -578,7 +578,7 @@ The `aws iam create-access-key` command will output the secret access key and th ## Extras We are adding additional checks to improve the information gather from each account, these checks are out of the scope of the CIS benchmark for AWS but we consider them very helpful to get to know each AWS account set up and find issues on it. -At this momment we have 5 extra checks: +At this moment we have 8 extra checks: - 7.1 (`extra71`) Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark) - 7.2 (`extra72`) Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark) @@ -589,10 +589,11 @@ At this momment we have 5 extra checks: - 7.7 (`extra77`) Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark) - 7.8 (`extra78`) Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark) +To check all extras in one command: ``` ./prowler -c extras ``` -or to run just one of the checks, to see if you have S3 buckets open: +or to run just one of the checks: ``` ./prowler -c extraNUMBER ``` From 189462c474e7750f73441565d0feeb43ec356757 Mon Sep 17 00:00:00 2001 From: neonbunny Date: Tue, 16 Jan 2018 16:31:31 +0000 Subject: [PATCH 28/61] Fix typos in messages --- prowler | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prowler b/prowler index cd251b02..fdc985e5 100755 --- a/prowler +++ b/prowler @@ -1692,9 +1692,9 @@ extra74(){ for SG_ID in $LIST_OF_SECURITYGROUPS; do SG_NO_INGRESS_FILTER=$($AWSCLI ec2 describe-network-interfaces $PROFILE_OPT --region $regx --filters "Name=group-id,Values=$SG_ID" --query "length(NetworkInterfaces)" --output text) if [[ $SG_NO_INGRESS_FILTER -ne 0 ]];then - textWarn "$regx: $SG_ID has not ingress filtering and it is being used!" "$regx" + textWarn "$regx: $SG_ID has no ingress filtering and it is being used!" "$regx" else - textNotice "$regx: $SG_ID has not ingress filtering but it is no being used" "$regx" + textNotice "$regx: $SG_ID has no ingress filtering but it is not being used" "$regx" fi done done From 9c19e7b85dda8ab21dc6f6e94c31b7227bedf1c3 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Sat, 27 Jan 2018 10:35:25 -0500 Subject: [PATCH 29/61] improved load of auth credentials --- prowler | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/prowler b/prowler index fdc985e5..30f4a80f 100755 --- a/prowler +++ b/prowler @@ -239,24 +239,20 @@ else exit $EXITCODE fi -# Check environment if profile provided reads it from creds file, then instance profile -# if not profile provided loads it from environment variables +# It checks -p optoin first and use it as profile, if not -p provided then +# check environment variables and if not, it checks and loads credentials from +# instance profile (metadata server) if runs in an EC2 instance if [[ $PROFILE ]]; then PROFILE_OPT="--profile $PROFILE" - if [[ ! -f ~/.aws/credentials ]]; then - echo -e "\n$RED ERROR!$NORMAL AWS credentials file not found (~/.aws/credentials). Run 'aws configure' first. \n" - return 1 - else - # if Prowler runs insinde an AWS instance with IAM instance profile attached - INSTANCE_PROFILE=$(curl -s -m 1 http://169.254.169.254/latest/meta-data/iam/security-credentials/) - if [[ $INSTANCE_PROFILE ]]; then - AWS_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep AccessKeyId | cut -d':' -f2 | sed 's/[^0-9A-Z]*//g') - AWS_SECRET_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep SecretAccessKey | cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g') - AWS_SESSION_TOKEN=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} grep Token| cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g') - fi - fi else + # if Prowler runs insinde an AWS instance with IAM instance profile attached + INSTANCE_PROFILE=$(curl -s -m 1 http://169.254.169.254/latest/meta-data/iam/security-credentials/) + if [[ $INSTANCE_PROFILE ]]; then + AWS_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep AccessKeyId | cut -d':' -f2 | sed 's/[^0-9A-Z]*//g') + AWS_SECRET_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep SecretAccessKey | cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g') + AWS_SESSION_TOKEN=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} grep Token| cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g') + fi if [[ $AWS_ACCESS_KEY_ID && $AWS_SECRET_ACCESS_KEY || $AWS_SESSION_TOKEN ]];then PROFILE="ENV" PROFILE_OPT="" @@ -1692,9 +1688,9 @@ extra74(){ for SG_ID in $LIST_OF_SECURITYGROUPS; do SG_NO_INGRESS_FILTER=$($AWSCLI ec2 describe-network-interfaces $PROFILE_OPT --region $regx --filters "Name=group-id,Values=$SG_ID" --query "length(NetworkInterfaces)" --output text) if [[ $SG_NO_INGRESS_FILTER -ne 0 ]];then - textWarn "$regx: $SG_ID has no ingress filtering and it is being used!" "$regx" + textWarn "$regx: $SG_ID has not ingress filtering and it is being used!" "$regx" else - textNotice "$regx: $SG_ID has no ingress filtering but it is not being used" "$regx" + textNotice "$regx: $SG_ID has not ingress filtering but it is no being used" "$regx" fi done done From 398e55f21a15b02d557c4f374e8b919f6caa5cda Mon Sep 17 00:00:00 2001 From: Geoff Webster Date: Wed, 31 Jan 2018 15:38:56 -0800 Subject: [PATCH 30/61] added extra79 to check for internet facing ELBs. --- README.md | 5 +++-- prowler | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5112fb40..338f63f9 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ It covers hardening and security best practices for all AWS regions related to: - Logging (8 checks) - Monitoring (15 checks) - Networking (5 checks) -- Extras (8 checks) *see Extras section +- Extras (9 checks) *see Extras section For a comprehesive list and resolution look at the guide on the link above. @@ -578,7 +578,7 @@ The `aws iam create-access-key` command will output the secret access key and th ## Extras We are adding additional checks to improve the information gather from each account, these checks are out of the scope of the CIS benchmark for AWS but we consider them very helpful to get to know each AWS account set up and find issues on it. -At this moment we have 8 extra checks: +At this moment we have 9 extra checks: - 7.1 (`extra71`) Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark) - 7.2 (`extra72`) Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark) @@ -588,6 +588,7 @@ At this moment we have 8 extra checks: - 7.6 (`extra76`) Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark) - 7.7 (`extra77`) Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark) - 7.8 (`extra78`) Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark) +- 7.9 (`extra79`) Check for internet accessible Elastic Load Blancers (Not Scored) (Not part of CIS benchmark) To check all extras in one command: ``` diff --git a/prowler b/prowler index 30f4a80f..5cdae1e9 100755 --- a/prowler +++ b/prowler @@ -480,6 +480,8 @@ ID77="7.7,7.07" TITLE77="Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark)" ID78="7.8,7.08" TITLE78="Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark)" +ID79="7.9,7.09" +TITLE79="Check for internet facing Elastic Load Balancers (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { @@ -1767,6 +1769,22 @@ extra78(){ done } +extra79(){ + # "Check for internet facing Elastic Load Balancers (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID79" "$TITLE79" "NOT_SCORED" "EXTRA" + textNotice "Looking for Elastic Load Balancers in all regions... " + for regx in $REGIONS; do + LIST_OF_PUBLIC_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].LoadBalancerName' --output text) + if [[ $LIST_OF_PUBLIC_ELBS ]];then + for elb in $(echo $LIST_OF_PUBLIC_ELBS);do + textWarn "$regx: ELB: $elb is internet-facing!" "$regx" + done + else + textOK "$regx: no Internet Facing ELBs found" "$regx" + fi + done +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -1830,6 +1848,7 @@ callCheck(){ extra76|extra706 ) extra76;; extra77|extra707 ) extra77;; extra78|extra708 ) extra78;; + extra79|extra709 ) extra79;; ## Groups of Checks check1 ) @@ -1865,7 +1884,7 @@ callCheck(){ check43;check44;check45 ;; extras ) - extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78 + extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78;extra79 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -1944,6 +1963,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID76" "$TITLE76" "NOT_SCORED" "EXTRA" textTitle "$ID77" "$TITLE77" "NOT_SCORED" "EXTRA" textTitle "$ID78" "$TITLE78" "NOT_SCORED" "EXTRA" + textTitle "$ID79" "$TITLE79" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2028,6 +2048,7 @@ extra75 extra76 extra77 extra78 +extra79 cleanTemp exit $EXITCODE From 33029e35d5ea5e990b6507b3387747e6bdefd239 Mon Sep 17 00:00:00 2001 From: Geoff Webster Date: Wed, 31 Jan 2018 17:20:46 -0800 Subject: [PATCH 31/61] Added extra710 to check for internet facing EC2 Instances. --- README.md | 7 ++++--- prowler | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 338f63f9..6f305c71 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ It covers hardening and security best practices for all AWS regions related to: - Logging (8 checks) - Monitoring (15 checks) - Networking (5 checks) -- Extras (9 checks) *see Extras section +- Extras (10 checks) *see Extras section For a comprehesive list and resolution look at the guide on the link above. @@ -578,7 +578,7 @@ The `aws iam create-access-key` command will output the secret access key and th ## Extras We are adding additional checks to improve the information gather from each account, these checks are out of the scope of the CIS benchmark for AWS but we consider them very helpful to get to know each AWS account set up and find issues on it. -At this moment we have 9 extra checks: +At this moment we have 10 extra checks: - 7.1 (`extra71`) Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark) - 7.2 (`extra72`) Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark) @@ -588,7 +588,8 @@ At this moment we have 9 extra checks: - 7.6 (`extra76`) Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark) - 7.7 (`extra77`) Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark) - 7.8 (`extra78`) Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark) -- 7.9 (`extra79`) Check for internet accessible Elastic Load Blancers (Not Scored) (Not part of CIS benchmark) +- 7.9 (`extra79`) Check for internet facing Elastic Load Blancers (Not Scored) (Not part of CIS benchmark) +- 7.10 (`extra710`) Check for internet facing EC2 Instances (Not Scored) (Not part of CIS benchmark) To check all extras in one command: ``` diff --git a/prowler b/prowler index 5cdae1e9..76e82e74 100755 --- a/prowler +++ b/prowler @@ -482,6 +482,8 @@ ID78="7.8,7.08" TITLE78="Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark)" ID79="7.9,7.09" TITLE79="Check for internet facing Elastic Load Balancers (Not Scored) (Not part of CIS benchmark)" +ID710="7.10,7.10" +TITLE710="Check for internet facing EC2 Instances (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { @@ -1785,6 +1787,22 @@ extra79(){ done } +extra710(){ + # "Check for internet facing EC2 Instances (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID710" "$TITLE710" "NOT_SCORED" "EXTRA" + textNotice "Looking for instances in all regions... " + for regx in $REGIONS; do + LIST_OF_PUBLIC_INSTANCES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx --query 'Reservations[*].Instances[?PublicIpAddress].[InstanceId]' --output text) + if [[ $LIST_OF_PUBLIC_INSTANCES ]];then + for instance in $(echo $LIST_OF_PUBLIC_INSTANCES);do + textWarn "$regx: Instance: $instance is internet-facing!" "$regx" + done + else + textOK "$regx: no Internet Facing EC2 Instances found" "$regx" + fi + done +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -1849,6 +1867,7 @@ callCheck(){ extra77|extra707 ) extra77;; extra78|extra708 ) extra78;; extra79|extra709 ) extra79;; + extra710|extra710 ) extra710;; ## Groups of Checks check1 ) @@ -1884,7 +1903,8 @@ callCheck(){ check43;check44;check45 ;; extras ) - extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78;extra79 + extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; + extra79;extra710 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -1964,6 +1984,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID77" "$TITLE77" "NOT_SCORED" "EXTRA" textTitle "$ID78" "$TITLE78" "NOT_SCORED" "EXTRA" textTitle "$ID79" "$TITLE79" "NOT_SCORED" "EXTRA" + textTitle "$ID710" "$TITLE710" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2049,6 +2070,7 @@ extra76 extra77 extra78 extra79 +extra710 cleanTemp exit $EXITCODE From db4a91f623c941d042815af292059b74374c8749 Mon Sep 17 00:00:00 2001 From: Geoff Webster Date: Thu, 1 Feb 2018 14:48:33 -0800 Subject: [PATCH 32/61] Update extras 7.8-10 to display public endpoints as well as names. --- prowler | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/prowler b/prowler index 76e82e74..a7d17721 100755 --- a/prowler +++ b/prowler @@ -1760,11 +1760,13 @@ extra78(){ textTitle "$ID78" "$TITLE78" "NOT_SCORED" "EXTRA" textNotice "Looking for RDS instances in all regions... " for regx in $REGIONS; do - LIST_OF_RDS_PUBLIC_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[?PubliclyAccessible==`true`].{id:DBInstanceIdentifier}' --output text) + LIST_OF_RDS_PUBLIC_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[?PubliclyAccessible==`true`].[DBInstanceIdentifier,Endpoint.Address]' --output text) if [[ $LIST_OF_RDS_PUBLIC_INSTANCES ]];then - for rds_instance in $(echo $LIST_OF_RDS_PUBLIC_INSTANCES);do - textWarn "$regx: RDS instance $rds_instance is set as Publicly Accessible!" "$regx" - done + while read -r rds_instance;do + RDS_NAME=$(echo $rds_instance | awk '{ print $1; }') + RDS_DNSNAME=$(echo $rds_instance | awk '{ print $2; }') + textWarn "$regx: RDS instance: $RDS_NAME at $RDS_DNSNAME is set as Publicly Accessible!" "$regx" + done <<< "$LIST_OF_RDS_PUBLIC_INSTANCES" else textOK "$regx: no Publicly Accessible RDS instances found" "$regx" fi @@ -1776,11 +1778,13 @@ extra79(){ textTitle "$ID79" "$TITLE79" "NOT_SCORED" "EXTRA" textNotice "Looking for Elastic Load Balancers in all regions... " for regx in $REGIONS; do - LIST_OF_PUBLIC_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].LoadBalancerName' --output text) + LIST_OF_PUBLIC_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].[LoadBalancerName,DNSName]' --output text) if [[ $LIST_OF_PUBLIC_ELBS ]];then - for elb in $(echo $LIST_OF_PUBLIC_ELBS);do - textWarn "$regx: ELB: $elb is internet-facing!" "$regx" - done + while read -r elb;do + ELB_NAME=$(echo $elb | awk '{ print $1; }') + ELB_DNSNAME=$(echo $elb | awk '{ print $2; }') + textWarn "$regx: ELB: $ELB_NAME at DNS: $ELB_DNSNAME is internet-facing!" "$regx" + done <<< "$LIST_OF_PUBLIC_ELBS" else textOK "$regx: no Internet Facing ELBs found" "$regx" fi @@ -1792,11 +1796,13 @@ extra710(){ textTitle "$ID710" "$TITLE710" "NOT_SCORED" "EXTRA" textNotice "Looking for instances in all regions... " for regx in $REGIONS; do - LIST_OF_PUBLIC_INSTANCES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx --query 'Reservations[*].Instances[?PublicIpAddress].[InstanceId]' --output text) + LIST_OF_PUBLIC_INSTANCES=$($AWSCLI ec2 describe-instances $PROFILE_OPT --region $regx --query 'Reservations[*].Instances[?PublicIpAddress].[InstanceId,PublicIpAddress]' --output text) if [[ $LIST_OF_PUBLIC_INSTANCES ]];then - for instance in $(echo $LIST_OF_PUBLIC_INSTANCES);do - textWarn "$regx: Instance: $instance is internet-facing!" "$regx" - done + while read -r instance;do + INSTANCE_ID=$(echo $instance | awk '{ print $1; }') + PUBLIC_IP=$(echo $instance | awk '{ print $2; }') + textWarn "$regx: Instance: $INSTANCE_ID at IP: $PUBLIC_IP is internet-facing!" "$regx" + done <<< "$LIST_OF_PUBLIC_INSTANCES" else textOK "$regx: no Internet Facing EC2 Instances found" "$regx" fi From 64a11a344694f4c19990c7977fe4fba0c82c2cca Mon Sep 17 00:00:00 2001 From: Geoff Webster Date: Fri, 2 Feb 2018 10:59:45 -0800 Subject: [PATCH 33/61] Add check for publicly accessible redshift clusters. --- README.md | 10 +++++++--- prowler | 27 +++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6f305c71..10bdb469 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ It covers hardening and security best practices for all AWS regions related to: - Logging (8 checks) - Monitoring (15 checks) - Networking (5 checks) -- Extras (10 checks) *see Extras section +- Extras (11 checks) *see Extras section For a comprehesive list and resolution look at the guide on the link above. @@ -577,8 +577,11 @@ unset ACCOUNT_ID AWS_DEFAULT_PROFILE The `aws iam create-access-key` command will output the secret access key and the key id; keep these somewhere safe, and add them to ~/.aws/credentials with an appropriate profile name to use them with prowler. This is the only time they secret key will be shown. If you loose it, you will need to generate a replacement. ## Extras -We are adding additional checks to improve the information gather from each account, these checks are out of the scope of the CIS benchmark for AWS but we consider them very helpful to get to know each AWS account set up and find issues on it. -At this moment we have 10 extra checks: +We are adding additional checks to improve the information gather from each account, these checks are out of the scope of the CIS benchmark for AWS but we consider them very helpful to get to know each AWS account set up and find issues on it. + +Note: Some of these checks for publicly facing resources may not actually be fully public due to other layered controls like S3 Bucket Policies, Security Groups or Network ACLs. + +At this moment we have 11 extra checks: - 7.1 (`extra71`) Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark) - 7.2 (`extra72`) Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark) @@ -590,6 +593,7 @@ At this moment we have 10 extra checks: - 7.8 (`extra78`) Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark) - 7.9 (`extra79`) Check for internet facing Elastic Load Blancers (Not Scored) (Not part of CIS benchmark) - 7.10 (`extra710`) Check for internet facing EC2 Instances (Not Scored) (Not part of CIS benchmark) +- 7.11 (`extra711`) Check for Publicly Accessible Redshift Clusters (Not Scored) (Not part of CIS benchmark) To check all extras in one command: ``` diff --git a/prowler b/prowler index a7d17721..fcb358b8 100755 --- a/prowler +++ b/prowler @@ -241,7 +241,7 @@ fi # It checks -p optoin first and use it as profile, if not -p provided then # check environment variables and if not, it checks and loads credentials from -# instance profile (metadata server) if runs in an EC2 instance +# instance profile (metadata server) if runs in an EC2 instance if [[ $PROFILE ]]; then PROFILE_OPT="--profile $PROFILE" @@ -484,6 +484,8 @@ ID79="7.9,7.09" TITLE79="Check for internet facing Elastic Load Balancers (Not Scored) (Not part of CIS benchmark)" ID710="7.10,7.10" TITLE710="Check for internet facing EC2 Instances (Not Scored) (Not part of CIS benchmark)" +ID711="7.11,7.11" +TITLE711="Check for Publicly Accessible Redshift Clusters (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { @@ -1809,6 +1811,24 @@ extra710(){ done } +extra711(){ + # "Check for Publicly Accessible Redshift Clusters (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID711" "$TITLE711" "NOT_SCORED" "EXTRA" + textNotice "Looking for Reshift clusters in all regions... " + for regx in $REGIONS; do + LIST_OF_PUBLIC_REDSHIFT_CLUSTERS=$($AWSCLI redshift describe-clusters $PROFILE_OPT --region $regx --query 'Clusters[?PubliclyAccessible == `true`].[ClusterIdentifier,Endpoint.Address]' --output text) + if [[ $LIST_OF_PUBLIC_REDSHIFT_CLUSTERS ]];then + while read -r cluster;do + CLUSTER_ID=$(echo $cluster | awk '{ print $1; }') + CLUSTER_ENDPOINT=$(echo $cluster | awk '{ print $2; }') + textWarn "$regx: Cluster: $CLUSTER_ID at Endpoint: $CLUSTER_ENDPOINT is publicly accessible!" "$regx" + done <<< "$LIST_OF_PUBLIC_REDSHIFT_CLUSTERS" + else + textOK "$regx: no Publicly Accessible Redshift Clusters found" "$regx" + fi + done +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -1874,6 +1894,7 @@ callCheck(){ extra78|extra708 ) extra78;; extra79|extra709 ) extra79;; extra710|extra710 ) extra710;; + extra711|extra711 ) extra711;; ## Groups of Checks check1 ) @@ -1910,7 +1931,7 @@ callCheck(){ ;; extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; - extra79;extra710 + extra79;extra710;extra711 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -1991,6 +2012,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID78" "$TITLE78" "NOT_SCORED" "EXTRA" textTitle "$ID79" "$TITLE79" "NOT_SCORED" "EXTRA" textTitle "$ID710" "$TITLE710" "NOT_SCORED" "EXTRA" + textTitle "$ID711" "$TITLE711" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2077,6 +2099,7 @@ extra77 extra78 extra79 extra710 +extra711 cleanTemp exit $EXITCODE From e192a5ef44ac870bb3036c96febd72350b38064d Mon Sep 17 00:00:00 2001 From: Subramani Ramanathan Date: Mon, 5 Feb 2018 21:49:15 +0530 Subject: [PATCH 34/61] Update to 'check114' Updated 'check114' to ensure hardware MFA is enabled for root account by:- 1) Querying for 'SerialNumber' of the Virtual MFA Devices list 2) 'SerialNumber' is ARN for Virtual MFA Device and Device Number for Hardware MFA Device; so did grep for ARN with 'root-account-mfa-device' in the expression --- prowler | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prowler b/prowler index fcb358b8..eb6beb80 100755 --- a/prowler +++ b/prowler @@ -819,9 +819,9 @@ check114(){ COMMAND113=$($AWSCLI iam get-account-summary $PROFILE_OPT --region $REGION --output json --query 'SummaryMap.AccountMFAEnabled') textTitle "$ID114" "$TITLE114" "SCORED" "LEVEL1" if [ "$COMMAND113" == "1" ]; then - COMMAND114=$($AWSCLI iam list-virtual-mfa-devices $PROFILE_OPT --region $REGION --query 'VirtualMFADevices' --output text|grep :root |wc -l) - if [ "$COMMAND114" == "1" ]; then - textOK "Virtual MFA is enabled for root" + COMMAND114=$($AWSCLI iam list-virtual-mfa-devices $PROFILE_OPT --region $REGION --output text --assignment-status Assigned --query 'VirtualMFADevices[*].[SerialNumber]' | grep '^arn:aws:iam::[0-9]\{12\}:mfa/root-account-mfa-device$') + if [[ "$COMMAND114" ]]; then + textWarn "Only Virtual MFA is enabled for root" else textOK "Hardware MFA is enabled for root " fi From 9f01be416ada60ddcc7f3de861410a2f33d84cbd Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Mon, 5 Feb 2018 22:02:53 -0500 Subject: [PATCH 35/61] Improved check31 issue #111 --- prowler | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/prowler b/prowler index eb6beb80..29aca926 100755 --- a/prowler +++ b/prowler @@ -1151,18 +1151,40 @@ check31(){ 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 }') - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | awk '/UnauthorizedOperation/ || /AccessDenied/ {print $1" "$2}') + #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 - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /UnauthorizedOperation/ || /AccessDenied/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textOK "CloudWatch group $group found with metric filters and alarms set for Unauthorized Operation and Access Denied" - else - textWarn "CloudWatch group $group found with metric filters but no alarms associated" - fi + for metric in $METRICFILTER_SET; do + #HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /UnauthorizedOperation/ || /AccessDenied/;') + HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | grep $metric) + if [[ $HAS_ALARM_ASSOCIATED ]];then + CHECK31OK="$CHECK31OK $group:$metric" + else + CHECK31WARN="$CHECK31WARN $group:$metric" + fi + done else - textWarn "CloudWatch group $group found but no metric filters or alarms associated" + CHECK31WARN="$CHECK31WARN $group" fi done + + if [[ $CHECK31OK ]]; then + for group in $CHECK31OK; do + metric=${group#*:} + group=${group%:*} + textOK "CloudWatch group $group found with metric filter $metric and alarms set for Unauthorized Operation and Access Denied" + done + else + for group in $CHECK31WARN; do + case $group in + *:*) metric=${group#*:} + group=${group%:*} + textWarn "CloudWatch group $group found with metric filter $metric but no alarms associated" + ;; + *) textWarn "CloudWatch group $group found but no metric filters or alarms associated" + esac + done + fi else textWarn "No CloudWatch group found for CloudTrail events" fi From db765e9c55918ddb122a891dde143a9b90d7560f Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Mon, 5 Feb 2018 22:26:03 -0500 Subject: [PATCH 36/61] Fixed typo in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 10bdb469..96b6046a 100644 --- a/README.md +++ b/README.md @@ -577,7 +577,7 @@ unset ACCOUNT_ID AWS_DEFAULT_PROFILE The `aws iam create-access-key` command will output the secret access key and the key id; keep these somewhere safe, and add them to ~/.aws/credentials with an appropriate profile name to use them with prowler. This is the only time they secret key will be shown. If you loose it, you will need to generate a replacement. ## Extras -We are adding additional checks to improve the information gather from each account, these checks are out of the scope of the CIS benchmark for AWS but we consider them very helpful to get to know each AWS account set up and find issues on it. +We are adding additional checks to improve the information gather from each account, these checks are out of the scope of the CIS benchmark for AWS but we consider them very helpful to get to know each AWS account set up and find issues on it. Note: Some of these checks for publicly facing resources may not actually be fully public due to other layered controls like S3 Bucket Policies, Security Groups or Network ACLs. @@ -591,7 +591,7 @@ At this moment we have 11 extra checks: - 7.6 (`extra76`) Ensure there are no EC2 AMIs set as Public (Not Scored) (Not part of CIS benchmark) - 7.7 (`extra77`) Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark) - 7.8 (`extra78`) Ensure there are no Public Accessible RDS instances (Not Scored) (Not part of CIS benchmark) -- 7.9 (`extra79`) Check for internet facing Elastic Load Blancers (Not Scored) (Not part of CIS benchmark) +- 7.9 (`extra79`) Check for internet facing Elastic Load Balancers (Not Scored) (Not part of CIS benchmark) - 7.10 (`extra710`) Check for internet facing EC2 Instances (Not Scored) (Not part of CIS benchmark) - 7.11 (`extra711`) Check for Publicly Accessible Redshift Clusters (Not Scored) (Not part of CIS benchmark) From 194eecb26911ceae4bd107f997d3efde3a88e523 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Mon, 5 Feb 2018 23:07:55 -0500 Subject: [PATCH 37/61] New forensics-ready check group and extra712 --- README.md | 12 +++++++++++- prowler | 24 ++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 96b6046a..02336e8a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ - [Screenshots](#screenshots) - [Troubleshooting](#troubleshooting) - [Extras](#extras) +- [Forensics ready checks](#forensics-ready-checks) - [Add Custom Checks](#add-custom-checks) - [Third Party Integrations](#third-party-integrations) @@ -24,7 +25,7 @@ It covers hardening and security best practices for all AWS regions related to: - Logging (8 checks) - Monitoring (15 checks) - Networking (5 checks) -- Extras (11 checks) *see Extras section +- Extras (12 checks) *see Extras section For a comprehesive list and resolution look at the guide on the link above. @@ -594,6 +595,8 @@ At this moment we have 11 extra checks: - 7.9 (`extra79`) Check for internet facing Elastic Load Balancers (Not Scored) (Not part of CIS benchmark) - 7.10 (`extra710`) Check for internet facing EC2 Instances (Not Scored) (Not part of CIS benchmark) - 7.11 (`extra711`) Check for Publicly Accessible Redshift Clusters (Not Scored) (Not part of CIS benchmark) +- 7.12 (`extra712`) Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark) + To check all extras in one command: ``` @@ -604,6 +607,13 @@ or to run just one of the checks: ./prowler -c extraNUMBER ``` +## Forensics Ready Checks + +With this group of checks, Prowler looks if each service with logging or audit capabilities has them enabled to ensure all needed evidences are recorded for an eventual digital forensic investigation in case of incident. The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: +``` +./prowler -c forensics-ready +``` + ## Add Custom Checks In order to add any new check feel free to create a new extra check in the extras section. To do so, you will need to follow these steps: diff --git a/prowler b/prowler index 29aca926..4a1b66a8 100755 --- a/prowler +++ b/prowler @@ -486,7 +486,8 @@ ID710="7.10,7.10" TITLE710="Check for internet facing EC2 Instances (Not Scored) (Not part of CIS benchmark)" ID711="7.11,7.11" TITLE711="Check for Publicly Accessible Redshift Clusters (Not Scored) (Not part of CIS benchmark)" - +ID712="7.12,7.12" +TITLE712="Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { >&2 echo "" @@ -1851,6 +1852,19 @@ extra711(){ done } +extra712(){ + # "Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID712" "$TITLE712" "NOT_SCORED" "EXTRA" + textNotice "No API commands available to check if Macie is enabled," + textNotice "just looking if IAM Macie related permissions exist. " + MACIE_IAM_ROLES_CREATED=$($AWSCLI iam list-roles $PROFILE_OPT --query 'Roles[*].Arn'|grep AWSMacieServiceCustomer|wc -l) + if [[ $MACIE_IAM_ROLES_CREATED -eq 2 ]];then + textOK "Macie related IAM roles exist, so it might be enabled. Check it out manually." + else + textWarn "No Macie related IAM roles found. It is most likely not be enabled" + fi +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -1917,6 +1931,7 @@ callCheck(){ extra79|extra709 ) extra79;; extra710|extra710 ) extra710;; extra711|extra711 ) extra711;; + extra712|extra712 ) extra712;; ## Groups of Checks check1 ) @@ -1953,7 +1968,12 @@ callCheck(){ ;; extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; - extra79;extra710;extra711 + extra79;extra710;extra711;extra712 + ;; + forensics-ready ) + check21;check22;check23;check24;check25;check26;check27; + check43; + extra712; ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; From d67170b87c6e1f64e1f8278a341706021b26348b Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Mon, 5 Feb 2018 23:11:43 -0500 Subject: [PATCH 38/61] New forensics-ready check group and extra712 --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 02336e8a..656e81ba 100644 --- a/README.md +++ b/README.md @@ -609,7 +609,18 @@ or to run just one of the checks: ## Forensics Ready Checks -With this group of checks, Prowler looks if each service with logging or audit capabilities has them enabled to ensure all needed evidences are recorded for an eventual digital forensic investigation in case of incident. The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: +With this group of checks, Prowler looks if each service with logging or audit capabilities has them enabled to ensure all needed evidences are recorded and collected for an eventual digital forensic investigation in case of incident. List of checks part of this group: +- 2.1 Ensure CloudTrail is enabled in all regions (Scored) +- 2.2 Ensure CloudTrail log file validation is enabled (Scored) +- 2.3 Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored) +- 2.4 Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored) +- 2.5 Ensure AWS Config is enabled in all regions (Scored) +- 2.6 Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored) +- 2.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored) +- 4.3 Ensure VPC Flow Logging is Enabled in all VPCs (Scored) +- 7.12 Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark) + +The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: ``` ./prowler -c forensics-ready ``` From 84591d25a69cd7cc924cefd5e99d0c1aa43161f3 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Mon, 5 Feb 2018 23:41:19 -0500 Subject: [PATCH 39/61] New check extra713 for GuardDuty --- README.md | 1 + prowler | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 656e81ba..7e109802 100644 --- a/README.md +++ b/README.md @@ -619,6 +619,7 @@ With this group of checks, Prowler looks if each service with logging or audit c - 2.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored) - 4.3 Ensure VPC Flow Logging is Enabled in all VPCs (Scored) - 7.12 Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark) +- 7.13 Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark) The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: ``` diff --git a/prowler b/prowler index 4a1b66a8..14d8ee65 100755 --- a/prowler +++ b/prowler @@ -488,6 +488,8 @@ ID711="7.11,7.11" TITLE711="Check for Publicly Accessible Redshift Clusters (Not Scored) (Not part of CIS benchmark)" ID712="7.12,7.12" TITLE712="Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark)" +ID713="7.13,7.13" +TITLE713="Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { >&2 echo "" @@ -1865,6 +1867,26 @@ extra712(){ fi } +extra713(){ + # "Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID713" "$TITLE713" "NOT_SCORED" "EXTRA" + for regx in $REGIONS; do + LIST_OF_GUARDDUTY_DETECTORS=$($AWSCLI guardduty list-detectors $PROFILE_OPT --region $regx --output text 2>/dev/null |cut -f2) + if [[ $LIST_OF_GUARDDUTY_DETECTORS ]];then + while read -r detector;do + DETECTOR_ENABLED=$($AWSCLI guardduty get-detector --detector-id $detector $PROFILE_OPT --region $regx --output text| cut -f3|grep ENABLED) + if [[ $DETECTOR_ENABLED ]]; then + textOK "$regx: GuardDuty detector $detector enabled" "$regx" + else + textWarn "$regx: GuardDuty detector $detector configured but suspended" "$regx" + fi + done <<< "$LIST_OF_GUARDDUTY_DETECTORS" + else + textWarn "$regx: GuardDuty detector $detector not configured" "$regx" + fi + done +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -1932,6 +1954,7 @@ callCheck(){ extra710|extra710 ) extra710;; extra711|extra711 ) extra711;; extra712|extra712 ) extra712;; + extra713|extra713 ) extra713;; ## Groups of Checks check1 ) @@ -1968,12 +1991,12 @@ callCheck(){ ;; extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; - extra79;extra710;extra711;extra712 + extra79;extra710;extra711;extra712;extra713 ;; forensics-ready ) check21;check22;check23;check24;check25;check26;check27; check43; - extra712; + extra712;extra713 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -2055,6 +2078,8 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID79" "$TITLE79" "NOT_SCORED" "EXTRA" textTitle "$ID710" "$TITLE710" "NOT_SCORED" "EXTRA" textTitle "$ID711" "$TITLE711" "NOT_SCORED" "EXTRA" + textTitle "$ID712" "$TITLE712" "NOT_SCORED" "EXTRA" + textTitle "$ID713" "$TITLE713" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2142,6 +2167,8 @@ extra78 extra79 extra710 extra711 +extra712 +extra713 cleanTemp exit $EXITCODE From 53580d488c4e32d1e30871f1f5cc43f1d97e37b0 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Wed, 7 Feb 2018 22:22:51 -0500 Subject: [PATCH 40/61] Fix issue #165 --- README.md | 3 ++- prowler | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7e109802..294768f7 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - [Screenshots](#screenshots) - [Troubleshooting](#troubleshooting) - [Extras](#extras) -- [Forensics ready checks](#forensics-ready-checks) +- [Forensics Ready Checks](#forensics-ready-checks) - [Add Custom Checks](#add-custom-checks) - [Third Party Integrations](#third-party-integrations) @@ -26,6 +26,7 @@ It covers hardening and security best practices for all AWS regions related to: - Monitoring (15 checks) - Networking (5 checks) - Extras (12 checks) *see Extras section +- Forensics related checks For a comprehesive list and resolution look at the guide on the link above. diff --git a/prowler b/prowler index 14d8ee65..527d90d2 100755 --- a/prowler +++ b/prowler @@ -167,7 +167,7 @@ fi SCRIPT_START_TIME=$( date -u +"%Y-%m-%dT%H:%M:%S%z" ) # Functions to manage dates depending on OS -if [[ "$OSTYPE" == "linux-gnu" ]]; then +if [ "$OSTYPE" == "linux-gnu" ] || [ "$OSTYPE" == "linux-musl" ]; then # function to compare in days, usage how_older_from_today date # date format %Y-%m-%d how_older_from_today() @@ -570,7 +570,7 @@ genCredReport() { # Save report to a file, decode it, deletion at finish and after every single check saveReport(){ - TEMP_REPORT_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-XXXXX.cred_report ) + TEMP_REPORT_FILE=$(mktemp -t -p /tmp prowler-${ACCOUNT_NUM}.cred_report-XXXXXX) $AWSCLI iam get-credential-report --query 'Content' --output text $PROFILE_OPT --region $REGION | decode_report > $TEMP_REPORT_FILE if [[ $KEEPCREDREPORT -eq 1 ]]; then textTitle "0.2" "Saving IAM Credential Report ..." "NOT_SCORED" "SUPPORT" From 9f977d263d0c4b2eaf6e4d0214ba944e49177c8c Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Wed, 7 Feb 2018 22:47:13 -0500 Subject: [PATCH 41/61] Fixed mktemp in OSX --- prowler | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prowler b/prowler index 527d90d2..02e0e3e9 100755 --- a/prowler +++ b/prowler @@ -168,6 +168,7 @@ SCRIPT_START_TIME=$( date -u +"%Y-%m-%dT%H:%M:%S%z" ) # Functions to manage dates depending on OS if [ "$OSTYPE" == "linux-gnu" ] || [ "$OSTYPE" == "linux-musl" ]; then + TEMP_REPORT_FILE=$(mktemp -t -p /tmp prowler.cred_report-XXXXXX) # function to compare in days, usage how_older_from_today date # date format %Y-%m-%d how_older_from_today() @@ -193,6 +194,7 @@ if [ "$OSTYPE" == "linux-gnu" ] || [ "$OSTYPE" == "linux-musl" ]; then } elif [[ "$OSTYPE" == "darwin"* ]]; then # BSD/OSX commands compatibility + TEMP_REPORT_FILE=$(mktemp -t prowler.cred_report-XXXXXX) how_older_from_today() { DATE_TO_COMPARE=$1 @@ -570,7 +572,6 @@ genCredReport() { # Save report to a file, decode it, deletion at finish and after every single check saveReport(){ - TEMP_REPORT_FILE=$(mktemp -t -p /tmp prowler-${ACCOUNT_NUM}.cred_report-XXXXXX) $AWSCLI iam get-credential-report --query 'Content' --output text $PROFILE_OPT --region $REGION | decode_report > $TEMP_REPORT_FILE if [[ $KEEPCREDREPORT -eq 1 ]]; then textTitle "0.2" "Saving IAM Credential Report ..." "NOT_SCORED" "SUPPORT" @@ -581,6 +582,7 @@ saveReport(){ # Delete temporary report file cleanTemp(){ if [[ $KEEPCREDREPORT -ne 1 ]]; then + cd /tmp rm -fr $TEMP_REPORT_FILE fi } From c447e456d0cd6930377a8f690ac6ddf09b64bb18 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Wed, 7 Feb 2018 22:48:02 -0500 Subject: [PATCH 42/61] Fixed mktemp in OSX --- prowler | 1 - 1 file changed, 1 deletion(-) diff --git a/prowler b/prowler index 02e0e3e9..1008b775 100755 --- a/prowler +++ b/prowler @@ -582,7 +582,6 @@ saveReport(){ # Delete temporary report file cleanTemp(){ if [[ $KEEPCREDREPORT -ne 1 ]]; then - cd /tmp rm -fr $TEMP_REPORT_FILE fi } From b92ba781ba3f4bb8dbb10a8a43bcdf625f27f922 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Wed, 7 Feb 2018 22:52:32 -0500 Subject: [PATCH 43/61] Fixed mktemp in OSX and OS handlign --- prowler | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prowler b/prowler index 1008b775..49a3ba9f 100755 --- a/prowler +++ b/prowler @@ -216,6 +216,7 @@ elif [[ "$OSTYPE" == "darwin"* ]]; then } elif [[ "$OSTYPE" == "cygwin" ]]; then # POSIX compatibility layer and Linux environment emulation for Windows + TEMP_REPORT_FILE=$(mktemp -t -p /tmp prowler.cred_report-XXXXXX) how_older_from_today() { DATE_TO_COMPARE=$1 @@ -236,7 +237,8 @@ elif [[ "$OSTYPE" == "cygwin" ]]; then base64 -d } else - echo "Unknown Operating System" + echo "Unknown Operating System! Valid \$OSTYPE: linux-gnu, linux-musl, darwin* or cygwin" + echo "Found: $OSTYPE" EXITCODE=1 exit $EXITCODE fi From 3665d64f2b4182db9bf400def6b878cba88e51db Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Wed, 7 Feb 2018 23:49:26 -0500 Subject: [PATCH 44/61] Added check extra714 CloudFront logging --- README.md | 6 ++++-- prowler | 29 +++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 294768f7..0bd66e29 100644 --- a/README.md +++ b/README.md @@ -583,7 +583,7 @@ We are adding additional checks to improve the information gather from each acco Note: Some of these checks for publicly facing resources may not actually be fully public due to other layered controls like S3 Bucket Policies, Security Groups or Network ACLs. -At this moment we have 11 extra checks: +At this moment we have 14 extra checks: - 7.1 (`extra71`) Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark) - 7.2 (`extra72`) Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark) @@ -597,7 +597,8 @@ At this moment we have 11 extra checks: - 7.10 (`extra710`) Check for internet facing EC2 Instances (Not Scored) (Not part of CIS benchmark) - 7.11 (`extra711`) Check for Publicly Accessible Redshift Clusters (Not Scored) (Not part of CIS benchmark) - 7.12 (`extra712`) Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark) - +- 7.13 (`extra713`) Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark) +- 7.14 (`extra714`) Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark) To check all extras in one command: ``` @@ -621,6 +622,7 @@ With this group of checks, Prowler looks if each service with logging or audit c - 4.3 Ensure VPC Flow Logging is Enabled in all VPCs (Scored) - 7.12 Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark) - 7.13 Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark) +- 7.14 Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark) The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: ``` diff --git a/prowler b/prowler index 49a3ba9f..8e530619 100755 --- a/prowler +++ b/prowler @@ -494,6 +494,8 @@ ID712="7.12,7.12" TITLE712="Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark)" ID713="7.13,7.13" TITLE713="Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark)" +ID714="7.14,7.14" +TITLE714="Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { >&2 echo "" @@ -1890,6 +1892,26 @@ extra713(){ done } +extra714(){ + # "Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID714" "$TITLE714" "NOT_SCORED" "EXTRA" + for regx in $REGIONS; do + LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --region $regx --query 'DistributionList.Items[].Id' --output text |grep -v "^None") + if [[ $LIST_OF_DISTRIBUTIONS ]]; then + for cdn in $LIST_OF_DISTRIBUTIONS;do + CDN_LOG_ENABLED=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --region $regx --id "$cdn" --query 'Distribution.DistributionConfig.Logging.Enabled' | grep true) + if [[ $CDN_LOG_ENABLED ]];then + textOK "$regx: CDN $cdn logging enabled" "$regx" + else + textWarn "$regx: CDN $cdn logging disabled!" "$regx" + fi + done + else + textOK "$regx: CDN not configured" "$regx" + fi + done +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -1958,6 +1980,7 @@ callCheck(){ extra711|extra711 ) extra711;; extra712|extra712 ) extra712;; extra713|extra713 ) extra713;; + extra714|extra714 ) extra714;; ## Groups of Checks check1 ) @@ -1994,12 +2017,12 @@ callCheck(){ ;; extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; - extra79;extra710;extra711;extra712;extra713 + extra79;extra710;extra711;extra712;extra713;extra714 ;; forensics-ready ) check21;check22;check23;check24;check25;check26;check27; check43; - extra712;extra713 + extra712;extra713;extra714 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -2083,6 +2106,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID711" "$TITLE711" "NOT_SCORED" "EXTRA" textTitle "$ID712" "$TITLE712" "NOT_SCORED" "EXTRA" textTitle "$ID713" "$TITLE713" "NOT_SCORED" "EXTRA" + textTitle "$ID714" "$TITLE713" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2172,6 +2196,7 @@ extra710 extra711 extra712 extra713 +extra714 cleanTemp exit $EXITCODE From 841e5436b967b4cc2279c21f489ef5a3a3aa30ff Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Thu, 8 Feb 2018 00:27:27 -0500 Subject: [PATCH 45/61] Added new check extra715 ES service logging --- README.md | 5 ++++- prowler | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0bd66e29..0d789bee 100644 --- a/README.md +++ b/README.md @@ -583,7 +583,7 @@ We are adding additional checks to improve the information gather from each acco Note: Some of these checks for publicly facing resources may not actually be fully public due to other layered controls like S3 Bucket Policies, Security Groups or Network ACLs. -At this moment we have 14 extra checks: +At this moment we have 15 extra checks: - 7.1 (`extra71`) Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark) - 7.2 (`extra72`) Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark) @@ -599,6 +599,8 @@ At this moment we have 14 extra checks: - 7.12 (`extra712`) Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark) - 7.13 (`extra713`) Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark) - 7.14 (`extra714`) Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.15 (`extra715`) Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark) + To check all extras in one command: ``` @@ -623,6 +625,7 @@ With this group of checks, Prowler looks if each service with logging or audit c - 7.12 Check if Amazon Macie is enabled (Not Scored) (Not part of CIS benchmark) - 7.13 Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark) - 7.14 Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.15 Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark) The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: ``` diff --git a/prowler b/prowler index 8e530619..141fb6b8 100755 --- a/prowler +++ b/prowler @@ -496,6 +496,8 @@ ID713="7.13,7.13" TITLE713="Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark)" ID714="7.14,7.14" TITLE714="Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark)" +ID715="7.15,7.15" +TITLE715="Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { >&2 echo "" @@ -1912,6 +1914,32 @@ extra714(){ done } +extra715(){ + # "Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID715" "$TITLE715" "NOT_SCORED" "EXTRA" + for regx in $REGIONS; do + LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query DomainNames --output text) + if [[ $LIST_OF_DOMAINS ]]; then + for domain in $LIST_OF_DOMAINS;do + SEARCH_SLOWLOG_ENABLED=$($AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.LogPublishingOptions.Options.SEARCH_SLOW_LOGS.Enabled --output text |grep -v ^None|grep -v ^False) + if [[ $SEARCH_SLOWLOG_ENABLED ]];then + textOK "$regx: ElasticSearch Service domain $domain SEARCH_SLOW_LOGS enabled" "$regx" + else + textWarn "$regx: ElasticSearch Service domain $domain SEARCH_SLOW_LOGS disabled!" "$regx" + fi + INDEX_SLOWLOG_ENABLED=$($AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.LogPublishingOptions.Options.INDEX_SLOW_LOGS.Enabled --output text |grep -v ^None|grep -v ^False) + if [[ $INDEX_SLOWLOG_ENABLED ]];then + textOK "$regx: ElasticSearch Service domain $domain INDEX_SLOW_LOGS enabled" "$regx" + else + textWarn "$regx: ElasticSearch Service domain $domain INDEX_SLOW_LOGS disabled!" "$regx" + fi + done + else + textOK "$regx: No Elasticsearch Service domain found" "$regx" + fi + done +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -1981,6 +2009,7 @@ callCheck(){ extra712|extra712 ) extra712;; extra713|extra713 ) extra713;; extra714|extra714 ) extra714;; + extra715|extra715 ) extra715;; ## Groups of Checks check1 ) @@ -2017,12 +2046,12 @@ callCheck(){ ;; extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; - extra79;extra710;extra711;extra712;extra713;extra714 + extra79;extra710;extra711;extra712;extra713;extra714;extra715 ;; forensics-ready ) check21;check22;check23;check24;check25;check26;check27; check43; - extra712;extra713;extra714 + extra712;extra713;extra714;extra715 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -2106,7 +2135,8 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID711" "$TITLE711" "NOT_SCORED" "EXTRA" textTitle "$ID712" "$TITLE712" "NOT_SCORED" "EXTRA" textTitle "$ID713" "$TITLE713" "NOT_SCORED" "EXTRA" - textTitle "$ID714" "$TITLE713" "NOT_SCORED" "EXTRA" + textTitle "$ID714" "$TITLE714" "NOT_SCORED" "EXTRA" + textTitle "$ID715" "$TITLE715" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2197,6 +2227,7 @@ extra711 extra712 extra713 extra714 +extra715 cleanTemp exit $EXITCODE From 55d3d642f97f0fae0e9a157953cdaa87cddfc10f Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Thu, 8 Feb 2018 01:01:28 -0500 Subject: [PATCH 46/61] Added new check extra716 ES service allow open access --- README.md | 3 ++- prowler | 31 ++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0d789bee..b06ffbef 100644 --- a/README.md +++ b/README.md @@ -583,7 +583,7 @@ We are adding additional checks to improve the information gather from each acco Note: Some of these checks for publicly facing resources may not actually be fully public due to other layered controls like S3 Bucket Policies, Security Groups or Network ACLs. -At this moment we have 15 extra checks: +At this moment we have 16 extra checks: - 7.1 (`extra71`) Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark) - 7.2 (`extra72`) Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark) @@ -600,6 +600,7 @@ At this moment we have 15 extra checks: - 7.13 (`extra713`) Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark) - 7.14 (`extra714`) Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.15 (`extra715`) Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.16 (`extra716`) Check if Elasticsearch Service domains allow open access (Not Scored) (Not part of CIS benchmark) To check all extras in one command: diff --git a/prowler b/prowler index 141fb6b8..b00cb69b 100755 --- a/prowler +++ b/prowler @@ -498,6 +498,9 @@ ID714="7.14,7.14" TITLE714="Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark)" ID715="7.15,7.15" TITLE715="Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark)" +ID716="7.16,7.16" +TITLE716="Check if Elasticsearch Service domains allow open access (Not Scored) (Not part of CIS benchmark)" + printCsvHeader() { >&2 echo "" @@ -1940,6 +1943,29 @@ extra715(){ done } +extra716(){ + # "Check if Elasticsearch Service domains allow open access (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID716" "$TITLE716" "NOT_SCORED" "EXTRA" + for regx in $REGIONS; do + LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query DomainNames --output text) + if [[ $LIST_OF_DOMAINS ]]; then + for domain in $LIST_OF_DOMAINS;do + TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.policy.XXXXXXXXXX) + $AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.AccessPolicies.Options --output text > $TEMP_POLICY_FILE 2> /dev/null + # check if the policy has Principal as * + CHECK_ES_DOMAIN_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | awk '/Principal/ && !skip { print } { skip = /Deny/} '|grep \"Principal|grep \*) + if [[ $CHECK_ES_DOMAIN_ALLUSERS_POLICY ]];then + textWarn "$regx: $domain policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx" + else + textOK "$regx: $domain is not open" "$regx" + fi + done + fi + textOK "$regx: No Elasticsearch Service domain found" "$regx" + rm -fr $TEMP_POLICY_FILE + done +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -2010,6 +2036,7 @@ callCheck(){ extra713|extra713 ) extra713;; extra714|extra714 ) extra714;; extra715|extra715 ) extra715;; + extra716|extra716 ) extra716;; ## Groups of Checks check1 ) @@ -2046,7 +2073,7 @@ callCheck(){ ;; extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; - extra79;extra710;extra711;extra712;extra713;extra714;extra715 + extra79;extra710;extra711;extra712;extra713;extra714;extra715;extra716 ;; forensics-ready ) check21;check22;check23;check24;check25;check26;check27; @@ -2137,6 +2164,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID713" "$TITLE713" "NOT_SCORED" "EXTRA" textTitle "$ID714" "$TITLE714" "NOT_SCORED" "EXTRA" textTitle "$ID715" "$TITLE715" "NOT_SCORED" "EXTRA" + textTitle "$ID716" "$TITLE716" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2228,6 +2256,7 @@ extra712 extra713 extra714 extra715 +extra716 cleanTemp exit $EXITCODE From b2264997d61c8686bbbafe2ec63793256dace598 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Thu, 8 Feb 2018 01:21:22 -0500 Subject: [PATCH 47/61] Added new check extra716 ES service allow open access --- prowler | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/prowler b/prowler index b00cb69b..6a24f653 100755 --- a/prowler +++ b/prowler @@ -1950,14 +1950,19 @@ extra716(){ LIST_OF_DOMAINS=$($AWSCLI es list-domain-names $PROFILE_OPT --region $regx --query DomainNames --output text) if [[ $LIST_OF_DOMAINS ]]; then for domain in $LIST_OF_DOMAINS;do - TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.policy.XXXXXXXXXX) - $AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.AccessPolicies.Options --output text > $TEMP_POLICY_FILE 2> /dev/null - # check if the policy has Principal as * - CHECK_ES_DOMAIN_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | awk '/Principal/ && !skip { print } { skip = /Deny/} '|grep \"Principal|grep \*) - if [[ $CHECK_ES_DOMAIN_ALLUSERS_POLICY ]];then - textWarn "$regx: $domain policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx" + CHECK_IF_MEMBER_OF_VPC=$($AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.VPCOptions.Options.VPCId --output text|grep -v ^None) + if [[ ! $CHECK_IF_MEMBER_OF_VPC ]];then + TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-es-domain.policy.XXXXXXXXXX) + $AWSCLI es describe-elasticsearch-domain-config --domain-name $domain $PROFILE_OPT --region $regx --query DomainConfig.AccessPolicies.Options --output text > $TEMP_POLICY_FILE 2> /dev/null + # check if the policy has Principal as * + CHECK_ES_DOMAIN_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | awk '/Principal/ && !skip { print } { skip = /Deny/} '|grep \"Principal|grep \*) + if [[ $CHECK_ES_DOMAIN_ALLUSERS_POLICY ]];then + textWarn "$regx: $domain policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx" + else + textOK "$regx: $domain is not open" "$regx" + fi else - textOK "$regx: $domain is not open" "$regx" + textOK "$regx: $domain is in a VPC" "$regx" fi done fi From cd276ab959f596ad4e4964765e21adf7b81b8315 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Fri, 9 Feb 2018 17:17:09 -0500 Subject: [PATCH 48/61] Added new check extra717 ELB logging --- README.md | 4 +++- prowler | 60 ++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b06ffbef..a028ef10 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ It covers hardening and security best practices for all AWS regions related to: - Logging (8 checks) - Monitoring (15 checks) - Networking (5 checks) -- Extras (12 checks) *see Extras section +- Extras (17 checks) *see Extras section - Forensics related checks For a comprehesive list and resolution look at the guide on the link above. @@ -601,6 +601,7 @@ At this moment we have 16 extra checks: - 7.14 (`extra714`) Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.15 (`extra715`) Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.16 (`extra716`) Check if Elasticsearch Service domains allow open access (Not Scored) (Not part of CIS benchmark) +- 7.17 (`extra717`) Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark) To check all extras in one command: @@ -627,6 +628,7 @@ With this group of checks, Prowler looks if each service with logging or audit c - 7.13 Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark) - 7.14 Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.15 Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.17 Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark) The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: ``` diff --git a/prowler b/prowler index 6a24f653..8decceb6 100755 --- a/prowler +++ b/prowler @@ -500,7 +500,8 @@ ID715="7.15,7.15" TITLE715="Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark)" ID716="7.16,7.16" TITLE716="Check if Elasticsearch Service domains allow open access (Not Scored) (Not part of CIS benchmark)" - +ID717="7.17,7.17" +TITLE717="Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { >&2 echo "" @@ -1816,12 +1817,15 @@ extra79(){ textNotice "Looking for Elastic Load Balancers in all regions... " for regx in $REGIONS; do LIST_OF_PUBLIC_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].[LoadBalancerName,DNSName]' --output text) - if [[ $LIST_OF_PUBLIC_ELBS ]];then + LIST_OF_PUBLIC_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Scheme == `internet-facing`].[LoadBalancerName,DNSName]' --output text) + LIST_OF_ALL_ELBS=$( echo $LIST_OF_PUBLIC_ELBS; echo $LIST_OF_PUBLIC_ELBSV2) + LIST_OF_ALL_ELBS_PER_LINE=$( echo $LIST_OF_ALL_ELBS| xargs -n2 ) + if [[ $LIST_OF_ALL_ELBS ]];then while read -r elb;do ELB_NAME=$(echo $elb | awk '{ print $1; }') ELB_DNSNAME=$(echo $elb | awk '{ print $2; }') textWarn "$regx: ELB: $ELB_NAME at DNS: $ELB_DNSNAME is internet-facing!" "$regx" - done <<< "$LIST_OF_PUBLIC_ELBS" + done <<< "$LIST_OF_ALL_ELBS_PER_LINE" else textOK "$regx: no Internet Facing ELBs found" "$regx" fi @@ -1873,7 +1877,7 @@ extra712(){ if [[ $MACIE_IAM_ROLES_CREATED -eq 2 ]];then textOK "Macie related IAM roles exist, so it might be enabled. Check it out manually." else - textWarn "No Macie related IAM roles found. It is most likely not be enabled" + textWarn "No Macie related IAM roles found. It is most likely not to be enabled" fi } @@ -1912,7 +1916,7 @@ extra714(){ fi done else - textOK "$regx: CDN not configured" "$regx" + textNotice "$regx: No CDN configured" "$regx" fi done } @@ -1938,7 +1942,7 @@ extra715(){ fi done else - textOK "$regx: No Elasticsearch Service domain found" "$regx" + textNotice "$regx: No Elasticsearch Service domain found" "$regx" fi done } @@ -1966,11 +1970,45 @@ extra716(){ fi done fi - textOK "$regx: No Elasticsearch Service domain found" "$regx" + textNotice "$regx: No Elasticsearch Service domain found" "$regx" rm -fr $TEMP_POLICY_FILE done } +extra717(){ + # "Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID717" "$TITLE717" "NOT_SCORED" "EXTRA" + for regx in $REGIONS; do + LIST_OF_ELBS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[*].LoadBalancerName' --output text|xargs -n1) + LIST_OF_ELBSV2=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[*].LoadBalancerArn' --output text|xargs -n1) + if [[ $LIST_OF_ELBS || $LIST_OF_ELBSV2 ]]; then + if [[ $LIST_OF_ELBS ]]; then + for elb in $LIST_OF_ELBS; do + CHECK_ELBS_LOG_ENABLED=$($AWSCLI elb describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-name $elb --query 'LoadBalancerAttributes.AccessLog.Enabled'|grep "^true") + if [[ $CHECK_ELBS_LOG_ENABLED ]]; then + textOK "$regx: $elb has access logs to S3 configured" "$regx" + else + textWarn "$regx: $elb has not access logs configured" "$regx" + fi + done + fi + if [[ $LIST_OF_ELBSV2 ]]; then + for elbarn in $LIST_OF_ELBSV2; do + CHECK_ELBSV2_LOG_ENABLED=$($AWSCLI elbv2 describe-load-balancer-attributes $PROFILE_OPT --region $regx --load-balancer-arn $elbarn --query Attributes[*] --output text|grep "^access_logs.s3.enabled"|cut -f2|grep true) + ELBV2_NAME=$(echo $elbarn|cut -d\/ -f3) + if [[ $CHECK_ELBSV2_LOG_ENABLED ]]; then + textOK "$regx: $ELBV2_NAME has access logs to S3 configured" "$regx" + else + textWarn "$regx: $ELBV2_NAME has not access logs configured" "$regx" + fi + done + fi + else + textNotice "$regx: No ELBs found" "$regx" + fi + done +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -2042,6 +2080,7 @@ callCheck(){ extra714|extra714 ) extra714;; extra715|extra715 ) extra715;; extra716|extra716 ) extra716;; + extra717|extra717 ) extra717;; ## Groups of Checks check1 ) @@ -2078,12 +2117,13 @@ callCheck(){ ;; extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; - extra79;extra710;extra711;extra712;extra713;extra714;extra715;extra716 + extra79;extra710;extra711;extra712;extra713;extra714;extra715;extra716; + extra717 ;; forensics-ready ) check21;check22;check23;check24;check25;check26;check27; check43; - extra712;extra713;extra714;extra715 + extra712;extra713;extra714;extra715;extra717 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -2170,6 +2210,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID714" "$TITLE714" "NOT_SCORED" "EXTRA" textTitle "$ID715" "$TITLE715" "NOT_SCORED" "EXTRA" textTitle "$ID716" "$TITLE716" "NOT_SCORED" "EXTRA" + textTitle "$ID717" "$TITLE717" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2262,6 +2303,7 @@ extra713 extra714 extra715 extra716 +extra717 cleanTemp exit $EXITCODE From 1cc7c7d3aac4067f9d90bc443bca3ad73b35a567 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Fri, 9 Feb 2018 17:18:43 -0500 Subject: [PATCH 49/61] Removed redundand info in README --- README.md | 253 ------------------------------------------------------ 1 file changed, 253 deletions(-) diff --git a/README.md b/README.md index a028ef10..0bc00148 100644 --- a/README.md +++ b/README.md @@ -150,259 +150,6 @@ USAGE: - Sample screnshot of single check for check 3.3: screenshot 2016-09-14 13 20 46 -- Sample of a full report: - -``` -$ ./prowler - _ - _ __ _ __ _____ _| | ___ _ __ - | '_ \| '__/ _ \ \ /\ / / |/ _ \ '__| - | |_) | | | (_) \ V V /| | __/ | - | .__/|_| \___/ \_/\_/ |_|\___|_| - |_| CIS based AWS Account Hardening Tool - - -Date: Wed Sep 14 13:30:13 EDT 2016 - -This report is being generated using credentials below: - -AWS-CLI Profile: [default] AWS Region: [us-east-1] - --------------------------------------------------------------------------------------- -| GetCallerIdentity | -+--------------+-------------------------------------------+-------------------------+ -| Account | Arn | UserId | -+--------------+-------------------------------------------+-------------------------+ -| XXXXXXXXXXXX| arn:aws:iam::XXXXXXXXXXXX:user/toni | XXXXXXXXXXXXXXXXXXXXX | -+--------------+-------------------------------------------+-------------------------+ - -Colors Code for results: INFORMATIVE, OK (RECOMMENDED VALUE), CRITICAL (FIX REQUIRED) - - -Generating AWS IAM Credential Report....COMPLETE - - - 1 Identity and Access Management ********************************* - - 1.1 Avoid the use of the root account (Scored). Last time root account was used - (password last used, access_key_1_last_used, access_key_2_last_used): - 2016-08-11T20:59:27+00:00, N/A, N/A - - 1.2 Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password (Scored) - List of users with Password enabled but MFA disabled: - toni - - 1.3 Ensure credentials unused for 90 days or greater are disabled (Scored) - User list: - toni - - 1.4 Ensure access keys are rotated every 90 days or less (Scored) - Users with access key 1 older than 90 days: - - Users with access key 2 older than 90 days: - - 1.5 Ensure IAM password policy requires at least one uppercase letter (Scored) - FALSE - - 1.6 Ensure IAM password policy require at least one lowercase letter (Scored) - FALSE - - 1.7 Ensure IAM password policy require at least one symbol (Scored) - FALSE - - 1.8 Ensure IAM password policy require at least one number (Scored) - FALSE - - 1.9 Ensure IAM password policy requires minimum length of 14 or greater (Scored) - FALSE - - 1.10 Ensure IAM password policy prevents password reuse (Scored) - FALSE - - 1.11 Ensure IAM password policy expires passwords within 90 days or less (Scored) - FALSE - - 1.12 Ensure no root account access key exists (Scored) - Found access key 1 - OK No access key 2 found - - 1.13 Ensure hardware MFA is enabled for the root account (Scored) - OK - - 1.14 Ensure security questions are registered in the AWS account (Not Scored) - No command available for check 1.14 - Login to the AWS Console as root, click on the Account - Name -> My Account -> Configure Security Challenge Questions - - 1.15 Ensure IAM policies are attached only to groups or roles (Scored) - Users with policy attached to them instead to groups: (it may take few seconds...) - toni - - - 2 Logging ******************************************************** - - 2.1 Ensure CloudTrail is enabled in all regions (Scored) - FALSE - - 2.2 Ensure CloudTrail log file validation is enabled (Scored) - FALSE - - 2.3 Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored) - WARNING! CloudTrail bucket doesn't exist! - - 2.4 Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored) - WARNING! No CloudTrail trails found! - - 2.5 Ensure AWS Config is enabled in all regions (Scored) - WARNING! Region ap-south-1 has AWS Config disabled or not configured - WARNING! Region eu-west-1 has AWS Config disabled or not configured - WARNING! Region ap-southeast-1 has AWS Config disabled or not configured - WARNING! Region ap-southeast-2 has AWS Config disabled or not configured - WARNING! Region eu-central-1 has AWS Config disabled or not configured - WARNING! Region ap-northeast-2 has AWS Config disabled or not configured - WARNING! Region ap-northeast-1 has AWS Config disabled or not configured - WARNING! Region us-east-1 has AWS Config disabled or not configured - WARNING! Region sa-east-1 has AWS Config disabled or not configured - WARNING! Region us-west-1 has AWS Config disabled or not configured - WARNING! Region us-west-2 has AWS Config disabled or not configured - - 2.6 Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored) - WARNING! CloudTrail bucket doesn't exist! - - 2.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored) - WARNING! CloudTrail bucket doesn't exist! - - 2.8 Ensure rotation for customer created CMKs is enabled (Scored) - Region ap-south-1 doesn't have encryption keys - Region eu-west-1 doesn't have encryption keys - Region ap-southeast-1 doesn't have encryption keys - Region ap-southeast-2 doesn't have encryption keys - Region eu-central-1 doesn't have encryption keys - Region ap-northeast-2 doesn't have encryption keys - Region ap-northeast-1 doesn't have encryption keys - WARNING! Key a0e988df-bc84-423f-996c-XXXX in Region us-east-1 is not set to rotate! - Region sa-east-1 doesn't have encryption keys - Region us-west-1 doesn't have encryption keys - Region us-west-2 doesn't have encryption keys - - - 3 Monitoring ***************************************************** - - 3.1 Ensure a log metric filter and alarm exist for unauthorized API calls (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.2 Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.3 Ensure a log metric filter and alarm exist for usage of root account (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.4 Ensure a log metric filter and alarm exist for IAM policy changes (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.5 Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.6 Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.7 Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.8 Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.9 Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.10 Ensure a log metric filter and alarm exist for security group changes (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.11 Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.12 Ensure a log metric filter and alarm exist for changes to network gateways (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.13 Ensure a log metric filter and alarm exist for route table changes (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.14 Ensure a log metric filter and alarm exist for VPC changes (Scored) - WARNING! No CloudWatch group found, no metric filters or alarms associated - - 3.15 Ensure security contact information is registered (Scored) - No command available for check 3.15 - Login to the AWS Console, click on My Account - Go to Alternate Contacts -> make sure Security section is filled - - 3.16 Ensure appropriate subscribers to each SNS topic (Not Scored) - Region ap-south-1 doesn't have topics - Region eu-west-1 doesn't have topics - Region ap-southeast-1 doesn't have topics - Region ap-southeast-2 doesn't have topics - Region eu-central-1 doesn't have topics - Region ap-northeast-2 doesn't have topics - Region ap-northeast-1 doesn't have topics - Region us-east-1 doesn't have topics - Region sa-east-1 doesn't have topics - Region us-west-1 doesn't have topics - Region us-west-2 doesn't have topics - - - 4 Networking ************************************************** - - 4.1 Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored) - OK, No Security Groups found in ap-south-1 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in eu-west-1 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in ap-southeast-1 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in ap-southeast-2 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in eu-central-1 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in ap-northeast-2 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in ap-northeast-1 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in us-east-1 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in sa-east-1 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in us-west-1 with port 22 TCP open to 0.0.0.0/0 - OK, No Security Groups found in us-west-2 with port 22 TCP open to 0.0.0.0/0 - - 4.2 Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored) - OK, No Security Groups found in ap-south-1 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in eu-west-1 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in ap-southeast-1 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in ap-southeast-2 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in eu-central-1 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in ap-northeast-2 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in ap-northeast-1 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in us-east-1 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in sa-east-1 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in us-west-1 with port 3389 TCP open to 0.0.0.0/0 - OK, No Security Groups found in us-west-2 with port 3389 TCP open to 0.0.0.0/0 - - 4.3 Ensure VPC Flow Logging is Enabled in all Applicable Regions (Scored) - WARNING! no VPCFlowLog has been found in Region ap-south-1 - WARNING! no VPCFlowLog has been found in Region eu-west-1 - WARNING! no VPCFlowLog has been found in Region ap-southeast-1 - WARNING! no VPCFlowLog has been found in Region ap-southeast-2 - WARNING! no VPCFlowLog has been found in Region eu-central-1 - WARNING! no VPCFlowLog has been found in Region ap-northeast-2 - WARNING! no VPCFlowLog has been found in Region ap-northeast-1 - WARNING! no VPCFlowLog has been found in Region us-east-1 - WARNING! no VPCFlowLog has been found in Region sa-east-1 - WARNING! no VPCFlowLog has been found in Region us-west-1 - WARNING! no VPCFlowLog has been found in Region us-west-2 - - 4.4 Ensure the default security group restricts all traffic (Scored) - WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region ap-south-1 - WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region eu-west-1 - WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region ap-southeast-1 - WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region ap-southeast-2 - WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region eu-central-1 - WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region ap-northeast-2 - WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region ap-northeast-1 - OK, no Default Security Groups open to 0.0.0.0 found in Region us-east-1 - WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region sa-east-1 - WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region us-west-1 - OK, no Default Security Groups open to 0.0.0.0 found in Region us-west-2 - - For more information and reference: https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf ``` From 1281867bd41eb34fa5e6c7b152ae3d506de59a8b Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Fri, 9 Feb 2018 17:19:48 -0500 Subject: [PATCH 50/61] Removed redundand info in README --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 0bc00148..1bfab97d 100644 --- a/README.md +++ b/README.md @@ -150,10 +150,6 @@ USAGE: - Sample screnshot of single check for check 3.3: screenshot 2016-09-14 13 20 46 - - For more information and reference: - https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf -``` - ## Troubleshooting ### STS expired token From f5ba67da8698bbf08cdc5a3f8382643cab141cf2 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Fri, 9 Feb 2018 18:15:06 -0500 Subject: [PATCH 51/61] Added check extra718 bucket server access logging --- README.md | 4 +++- prowler | 27 +++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1bfab97d..ade71fcc 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ It covers hardening and security best practices for all AWS regions related to: - Logging (8 checks) - Monitoring (15 checks) - Networking (5 checks) -- Extras (17 checks) *see Extras section +- Extras (18 checks) *see Extras section - Forensics related checks For a comprehesive list and resolution look at the guide on the link above. @@ -345,6 +345,7 @@ At this moment we have 16 extra checks: - 7.15 (`extra715`) Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.16 (`extra716`) Check if Elasticsearch Service domains allow open access (Not Scored) (Not part of CIS benchmark) - 7.17 (`extra717`) Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.18 (`extra718`) Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark) To check all extras in one command: @@ -372,6 +373,7 @@ With this group of checks, Prowler looks if each service with logging or audit c - 7.14 Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.15 Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.17 Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.18 Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark) The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: ``` diff --git a/prowler b/prowler index 8decceb6..0b91f640 100755 --- a/prowler +++ b/prowler @@ -502,6 +502,8 @@ ID716="7.16,7.16" TITLE716="Check if Elasticsearch Service domains allow open access (Not Scored) (Not part of CIS benchmark)" ID717="7.17,7.17" TITLE717="Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark)" +ID718="7.18,7.18" +TITLE718="Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { >&2 echo "" @@ -2009,6 +2011,24 @@ extra717(){ done } +extra718(){ + # "Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID718" "$TITLE718" "NOT_SCORED" "EXTRA" + LIST_OF_BUCKETS=$($AWSCLI s3api list-buckets $PROFILE_OPT --query Buckets[*].Name --output text|xargs -n1) + if [[ $LIST_OF_BUCKETS ]]; then + for bucket in $LIST_OF_BUCKETS;do + BUCKET_SERVER_LOG_ENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --query [LoggingEnabled] --output text|grep -v "^None$") + if [[ $BUCKET_SERVER_LOG_ENABLED ]];then + textOK "Bucket $bucket has server access logging enabled" + else + textWarn "Bucket $bucket has server access logging disabled!" + fi + done + else + textNotice "No S3 Buckets found" + fi +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -2081,6 +2101,7 @@ callCheck(){ extra715|extra715 ) extra715;; extra716|extra716 ) extra716;; extra717|extra717 ) extra717;; + extra718|extra718 ) extra718;; ## Groups of Checks check1 ) @@ -2118,12 +2139,12 @@ callCheck(){ extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; extra79;extra710;extra711;extra712;extra713;extra714;extra715;extra716; - extra717 + extra717;extra718 ;; forensics-ready ) check21;check22;check23;check24;check25;check26;check27; check43; - extra712;extra713;extra714;extra715;extra717 + extra712;extra713;extra714;extra715;extra717;extra718 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -2211,6 +2232,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID715" "$TITLE715" "NOT_SCORED" "EXTRA" textTitle "$ID716" "$TITLE716" "NOT_SCORED" "EXTRA" textTitle "$ID717" "$TITLE717" "NOT_SCORED" "EXTRA" + textTitle "$ID718" "$TITLE718" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2304,6 +2326,7 @@ extra714 extra715 extra716 extra717 +extra718 cleanTemp exit $EXITCODE From d101e2b3bf2ac83808d8583bdd793ca9a26760d0 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Fri, 9 Feb 2018 19:57:54 -0500 Subject: [PATCH 52/61] Added new check extra719 route53 query logging --- README.md | 4 +++- prowler | 27 +++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ade71fcc..bd0e2090 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ It covers hardening and security best practices for all AWS regions related to: - Logging (8 checks) - Monitoring (15 checks) - Networking (5 checks) -- Extras (18 checks) *see Extras section +- Extras (19 checks) *see Extras section - Forensics related checks For a comprehesive list and resolution look at the guide on the link above. @@ -346,6 +346,7 @@ At this moment we have 16 extra checks: - 7.16 (`extra716`) Check if Elasticsearch Service domains allow open access (Not Scored) (Not part of CIS benchmark) - 7.17 (`extra717`) Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.18 (`extra718`) Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.19 (`extra719`) Check if Route53 hosted zones are logging queries to CloudWatch Logs (Not Scored) (Not part of CIS benchmark) To check all extras in one command: @@ -374,6 +375,7 @@ With this group of checks, Prowler looks if each service with logging or audit c - 7.15 Check if Elasticsearch Service domains have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.17 Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.18 Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.19 Check if Route53 hosted zones are logging queries to CloudWatch Logs (Not Scored) (Not part of CIS benchmark) The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: ``` diff --git a/prowler b/prowler index 0b91f640..f9162adb 100755 --- a/prowler +++ b/prowler @@ -504,6 +504,8 @@ ID717="7.17,7.17" TITLE717="Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark)" ID718="7.18,7.18" TITLE718="Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark)" +ID719="7.19,7.19" +TITLE719="Check if Route53 hosted zones are logging queries to CloudWatch Logs (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { >&2 echo "" @@ -2029,6 +2031,24 @@ extra718(){ fi } +extra719(){ + # "Check if Route53 hosted zones are logging queries to CloudWatch Logs (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID719" "$TITLE719" "NOT_SCORED" "EXTRA" + LIST_OF_HOSTED_ZONES=$($AWSCLI route53 list-hosted-zones $PROFILE_OPT --query HostedZones[*].Id --output text|xargs -n1) + if [[ $LIST_OF_HOSTED_ZONES ]]; then + for hostedzoneid in $LIST_OF_HOSTED_ZONES;do + HOSTED_ZONE_QUERY_LOG_ENABLED=$($AWSCLI route53 list-query-logging-configs --hosted-zone-id $hostedzoneid $PROFILE_OPT --query QueryLoggingConfigs[*].CloudWatchLogsLogGroupArn --output text|cut -d: -f7) + if [[ $HOSTED_ZONE_QUERY_LOG_ENABLED ]];then + textOK "Route53 hosted zone Id $hostedzoneid has query logging enabled in Log Group $HOSTED_ZONE_QUERY_LOG_ENABLED" + else + textWarn "Route53 hosted zone Id $hostedzoneid has query logging disabled!" + fi + done + else + textNotice "No Route53 hosted zones found" + fi +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -2102,6 +2122,7 @@ callCheck(){ extra716|extra716 ) extra716;; extra717|extra717 ) extra717;; extra718|extra718 ) extra718;; + extra719|extra719 ) extra719;; ## Groups of Checks check1 ) @@ -2139,12 +2160,12 @@ callCheck(){ extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; extra79;extra710;extra711;extra712;extra713;extra714;extra715;extra716; - extra717;extra718 + extra717;extra718;extra719 ;; forensics-ready ) check21;check22;check23;check24;check25;check26;check27; check43; - extra712;extra713;extra714;extra715;extra717;extra718 + extra712;extra713;extra714;extra715;extra717;extra718;extra719 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -2233,6 +2254,7 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID716" "$TITLE716" "NOT_SCORED" "EXTRA" textTitle "$ID717" "$TITLE717" "NOT_SCORED" "EXTRA" textTitle "$ID718" "$TITLE718" "NOT_SCORED" "EXTRA" + textTitle "$ID719" "$TITLE719" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2327,6 +2349,7 @@ extra715 extra716 extra717 extra718 +extra719 cleanTemp exit $EXITCODE From f64fac3e1793119168ac3aaf68208e7bdef391af Mon Sep 17 00:00:00 2001 From: Subramani Ramanathan Date: Mon, 12 Feb 2018 18:43:20 +0530 Subject: [PATCH 53/61] Improved 'check31' Get the Metric Name using Log Group and Filter names and check the alarms associated with that metric. --- prowler | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prowler b/prowler index f9162adb..1197f3db 100755 --- a/prowler +++ b/prowler @@ -1168,13 +1168,13 @@ check31(){ CLOUDWATCH_GROUP=$($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 | awk -F: '{ print $4 }') + CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | grep $group | awk -F: '{ print $4 }') #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 - #HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /UnauthorizedOperation/ || /AccessDenied/;') - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | grep $metric) + 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 From 771cbf6b082f3d3834f54c478bd62fd770a2c398 Mon Sep 17 00:00:00 2001 From: Subramani Ramanathan Date: Mon, 12 Feb 2018 21:55:55 +0530 Subject: [PATCH 54/61] Fix to get CloudWatch Log Group Region Fix to get CloudWatch Log Group Region, when more than one log group names are there --- prowler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler b/prowler index 1197f3db..a1df8214 100755 --- a/prowler +++ b/prowler @@ -1168,7 +1168,7 @@ check31(){ CLOUDWATCH_GROUP=$($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 | grep $group | awk -F: '{ print $4 }') + 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 }') #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 From 6b759db0ae9580a1a84f4143cc1b8811f463821d Mon Sep 17 00:00:00 2001 From: Subramani Ramanathan Date: Tue, 13 Feb 2018 21:27:04 +0530 Subject: [PATCH 55/61] Fix printing texts when both Ok and Warn are present in 'check31' We may have groups in both Ok and Warn; so we have to go through both CHECK31OK and CHECK31WARN one by one. --- prowler | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prowler b/prowler index a1df8214..75c3c8f5 100755 --- a/prowler +++ b/prowler @@ -1192,7 +1192,8 @@ check31(){ group=${group%:*} textOK "CloudWatch group $group found with metric filter $metric and alarms set for Unauthorized Operation and Access Denied" done - else + fi + if [[ $CHECK31WARN ]]; then for group in $CHECK31WARN; do case $group in *:*) metric=${group#*:} From ec7930146bcbfff39ad5ce8c0ec6aa717a6d3f37 Mon Sep 17 00:00:00 2001 From: Toni de la Fuente Date: Fri, 16 Feb 2018 12:33:05 -0500 Subject: [PATCH 56/61] New checks and improvements --- README.md | 25 +++-- prowler | 201 +++++++++++++++++++++++++++------- prowler-policy-additions.json | 3 +- 3 files changed, 180 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index bd0e2090..0121f8fa 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ It covers hardening and security best practices for all AWS regions related to: - Logging (8 checks) - Monitoring (15 checks) - Networking (5 checks) -- Extras (19 checks) *see Extras section -- Forensics related checks +- Extras (22 checks) *see Extras section* +- Forensics related group of checks For a comprehesive list and resolution look at the guide on the link above. @@ -128,14 +128,19 @@ USAGE: prowler -p -r [ -h ] Options: -p specify your AWS profile to use (i.e.: default) - -r specify an AWS region to direct API requests to (i.e.: us-east-1), all regions are checked anyway - -c specify a check number or group from the AWS CIS benchmark (i.e.: check11 for check 1.1, check3 for entire section 3 or level1 for CIS Level 1 Profile Definitions) - -f specify an AWS region to run checks against (i.e.: us-west-1) + -r specify an AWS region to direct API requests to + (i.e.: us-east-1), all regions are checked anyway + -c specify a check number or group from the AWS CIS benchmark + (i.e.: "check11" for check 1.1, "check3" for entire section 3, "level1" for CIS Level 1 Profile Definitions or "forensics-ready") + -f specify an AWS region to run checks against + (i.e.: us-west-1) -m specify the maximum number of items to return for long-running requests (default: 100) -M output mode: text (defalut), mono, csv (separator is ","; data is on stdout; progress on stderr) -k keep the credential report - -n show check numbers to sort easier (i.e.: 1.01 instead of 1.1) + -n show check numbers to sort easier + (i.e.: 1.01 instead of 1.1) -l list all available checks only (does not perform any check) + -e exclude extras -h this help ``` @@ -326,7 +331,7 @@ We are adding additional checks to improve the information gather from each acco Note: Some of these checks for publicly facing resources may not actually be fully public due to other layered controls like S3 Bucket Policies, Security Groups or Network ACLs. -At this moment we have 16 extra checks: +At this moment we have 22 extra checks: - 7.1 (`extra71`) Ensure users with AdministratorAccess policy have MFA tokens enabled (Not Scored) (Not part of CIS benchmark) - 7.2 (`extra72`) Ensure there are no EBS Snapshots set as Public (Not Scored) (Not part of CIS benchmark) @@ -347,6 +352,9 @@ At this moment we have 16 extra checks: - 7.17 (`extra717`) Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.18 (`extra718`) Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark) - 7.19 (`extra719`) Check if Route53 hosted zones are logging queries to CloudWatch Logs (Not Scored) (Not part of CIS benchmark) +- 7.20 (`extra720`) Check if Lambda functions are being recorded by CloudTrail (Not Scored) (Not part of CIS benchmark) +- 7.21 (`extra721`) Check if Redshift cluster has audit logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.22 (`extra722`) Check if API Gateway has logging enabled (Not Scored) (Not part of CIS benchmark) To check all extras in one command: @@ -376,6 +384,9 @@ With this group of checks, Prowler looks if each service with logging or audit c - 7.17 Check if Elastic Load Balancers have logging enabled (Not Scored) (Not part of CIS benchmark) - 7.18 Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark) - 7.19 Check if Route53 hosted zones are logging queries to CloudWatch Logs (Not Scored) (Not part of CIS benchmark) +- 7.20 Check if Lambda functions are being recorded by CloudTrail (Not Scored) (Not part of CIS benchmark) +- 7.21 Check if Redshift cluster has audit logging enabled (Not Scored) (Not part of CIS benchmark) +- 7.22 Check if API Gateway has logging enabled (Not Scored) (Not part of CIS benchmark) The `forensics-ready` group of checks uses existing and extra checks. To get a forensics readiness report, run this command: ``` diff --git a/prowler b/prowler index a1df8214..d49581d6 100755 --- a/prowler +++ b/prowler @@ -45,22 +45,28 @@ usage(){ echo " USAGE: `basename $0` -p -r [ -h ] + Options: -p specify your AWS profile to use (i.e.: default) - -r specify an AWS region to direct API requests to (i.e.: us-east-1), all regions are checked anyway - -c specify a check number or group from the AWS CIS benchmark (i.e.: check11 for check 1.1, extra71, check3 for entire section 3 or level1 for CIS Level 1 Profile Definitions) - -f specify an AWS region to run checks against (i.e.: us-west-1) + -r specify an AWS region to direct API requests to + (i.e.: us-east-1), all regions are checked anyway + -c specify a check number or group from the AWS CIS benchmark + (i.e.: "check11" for check 1.1, "check3" for entire section 3, "level1" for CIS Level 1 Profile Definitions or "forensics-ready") + -f specify an AWS region to run checks against + (i.e.: us-west-1) -m specify the maximum number of items to return for long-running requests (default: 100) - -M output mode: text (default), mono, csv (separator is \"${SEP}\"; data is on stdout; progress on stderr) + -M output mode: text (defalut), mono, csv (separator is ","; data is on stdout; progress on stderr) -k keep the credential report - -n show check numbers to sort easier (i.e.: 1.01 instead of 1.1) + -n show check numbers to sort easier + (i.e.: 1.01 instead of 1.1) -l list all available checks only (does not perform any check) + -e exclude extras -h this help " exit } -while getopts ":hlkp:r:c:f:m:M:n" OPTION; do +while getopts ":hlkp:r:c:f:m:M:en" OPTION; do case $OPTION in h ) usage @@ -94,6 +100,9 @@ while getopts ":hlkp:r:c:f:m:M:n" OPTION; do n ) NUMERAL=1 ;; + e ) + EXTRAS=1 + ;; : ) echo "" echo "$OPTRED ERROR!$OPTNORMAL -$OPTARG requires an argument" @@ -506,6 +515,12 @@ ID718="7.18,7.18" TITLE718="Check if S3 buckets have server access logging enabled (Not Scored) (Not part of CIS benchmark)" ID719="7.19,7.19" TITLE719="Check if Route53 hosted zones are logging queries to CloudWatch Logs (Not Scored) (Not part of CIS benchmark)" +ID720="7.20,7.20" +TITLE720="Check if Lambda functions invoke API operations are being recorded by CloudTrail (Not Scored) (Not part of CIS benchmark)" +ID721="7.21,7.21" +TITLE721="Check if Redshift cluster has audit logging enabled (Not Scored) (Not part of CIS benchmark)" +ID722="7.22,7.22" +TITLE722="Check if API Gateway has logging enabled (Not Scored) (Not part of CIS benchmark)" printCsvHeader() { >&2 echo "" @@ -1192,7 +1207,8 @@ check31(){ group=${group%:*} textOK "CloudWatch group $group found with metric filter $metric and alarms set for Unauthorized Operation and Access Denied" done - else + fi + if [[ $CHECK31WARN ]]; then for group in $CHECK31WARN; do case $group in *:*) metric=${group#*:} @@ -1889,7 +1905,7 @@ extra713(){ # "Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark)" textTitle "$ID713" "$TITLE713" "NOT_SCORED" "EXTRA" for regx in $REGIONS; do - LIST_OF_GUARDDUTY_DETECTORS=$($AWSCLI guardduty list-detectors $PROFILE_OPT --region $regx --output text 2>/dev/null |cut -f2) + LIST_OF_GUARDDUTY_DETECTORS=$($AWSCLI guardduty list-detectors $PROFILE_OPT --region $regx --output text |cut -f2) if [[ $LIST_OF_GUARDDUTY_DETECTORS ]];then while read -r detector;do DETECTOR_ENABLED=$($AWSCLI guardduty get-detector --detector-id $detector $PROFILE_OPT --region $regx --output text| cut -f3|grep ENABLED) @@ -1900,7 +1916,7 @@ extra713(){ fi done <<< "$LIST_OF_GUARDDUTY_DETECTORS" else - textWarn "$regx: GuardDuty detector $detector not configured" "$regx" + textWarn "$regx: GuardDuty detector not configured!" "$regx" fi done } @@ -2049,6 +2065,96 @@ extra719(){ fi } +extra720(){ + # "Check if Lambda functions invoke API operations are being recorded by CloudTrail (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID720" "$TITLE720" "NOT_SCORED" "EXTRA" + for regx in $REGIONS; do + LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --query Functions[*].FunctionName --output text) + if [[ $LIST_OF_FUNCTIONS ]]; then + for lambdafunction in $LIST_OF_FUNCTIONS;do + LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query trailList[?HomeRegion==\`$regx\`].Name --output text) + if [[ $LIST_OF_TRAILS ]]; then + for trail in $LIST_OF_TRAILS; do + FUNCTION_ENABLED_IN_TRAIL=$($AWSCLI cloudtrail get-event-selectors $PROFILE_OPT --trail-name $trail --region $regx --query "EventSelectors[*].DataResources[?Type == \`AWS::Lambda::Function\`].Values" --output text |xargs -n1| grep -E "^arn:aws:lambda.*function:$lambdafunction$") + if [[ $FUNCTION_ENABLED_IN_TRAIL ]]; then + textOK "$regx: Lambda function $lambdafunction enabled in trail $trail" "$regx" + else + textWarn "$regx: Lambda function $lambdafunction NOT enabled in trail $trail" "$regx" + fi + done + # LIST_OF_MULTIREGION_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query "trailList[?IsMultiRegionTrail == \`true\`].Name" --output text) + # if [[ $LIST_OF_MULTIREGION_TRAILS ]]; then + # for trail in $LIST_OF_MULTIREGION_TRAILS; do + # REGION_OF_TRAIL=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query "trailList[?IsMultiRegionTrail == \`true\` && Name == \`$trail\` ].HomeRegion" --output text) + # FUNCTION_ENABLED_IN_THIS_REGION=$($AWSCLI cloudtrail get-event-selectors $PROFILE_OPT --trail-name $trail --region $REGION_OF_TRAIL --query "EventSelectors[*].DataResources[?Type == \`AWS::Lambda::Function\`].Values" --output text |xargs -n1| grep -E "^arn:aws:lambda.*function:$lambdafunction$") + # if [[ $FUNCTION_ENABLED_IN_THIS_REGION ]]; then + # textOK "$regx: Lambda function $lambdafunction enabled in trail $trail" "$regx" + # else + # textWarn "$regx: Lambda function $lambdafunction NOT enabled in trail $trail" "$regx" + # fi + # done + # else + # textWarn "$regx: Lambda function $lambdafunction is not being recorded!" "$regx" + # fi + else + textWarn "$regx: Lambda function $lambdafunction is not being recorded no CloudTrail found!" "$regx" + fi + done + else + textNotice "$regx: No Lambda functions found" "$regx" + fi + done +} + +extra721(){ + # "Check if Redshift cluster has audit logging enabled (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID721" "$TITLE721" "NOT_SCORED" "EXTRA" + for regx in $REGIONS; do + LIST_OF_REDSHIFT_CLUSTERS=$($AWSCLI redshift describe-clusters $PROFILE_OPT --region $regx --query 'Clusters[*].ClusterIdentifier' --output text) + if [[ $LIST_OF_REDSHIFT_CLUSTERS ]]; then + for redshiftcluster in $LIST_OF_REDSHIFT_CLUSTERS;do + REDSHIFT_LOG_ENABLED=$($AWSCLI redshift describe-logging-status $PROFILE_OPT --region $regx --cluster-identifier $redshiftcluster --query LoggingEnabled --output text | grep True) + if [[ $REDSHIFT_LOG_ENABLED ]];then + REDSHIFT_LOG_ENABLED_BUCKET=$($AWSCLI redshift describe-logging-status $PROFILE_OPT --region $regx --cluster-identifier $redshiftcluster --query BucketName --output text) + textOK "$regx: Redshift cluster $redshiftcluster has audit logging enabled to bucket $REDSHIFT_LOG_ENABLED_BUCKET" "$regx" + else + textWarn "$regx: Redshift cluster $redshiftcluster logging disabled!" "$regx" + fi + done + else + textNotice "$regx: No Redshift cluster configured" "$regx" + fi + done +} + +extra722(){ + # "Check if API Gateway has logging enabled (Not Scored) (Not part of CIS benchmark)" + textTitle "$ID722" "$TITLE722" "NOT_SCORED" "EXTRA" + for regx in $REGIONS; do + LIST_OF_API_GW=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query items[*].id --output text) + if [[ $LIST_OF_API_GW ]];then + for apigwid in $LIST_OF_API_GW;do + API_GW_NAME=$($AWSCLI apigateway get-rest-apis $PROFILE_OPT --region $regx --query "items[?id==\`$apigwid\`].name" --output text) + CHECK_STAGES_NAME=$($AWSCLI apigateway get-stages $PROFILE_OPT --region $regx --rest-api-id $apigwid --query "item[*].stageName" --output text) + if [[ $CHECK_STAGES_NAME ]];then + for stagname in $CHECK_STAGES_NAME;do + CHECK_STAGE_METHOD_LOGGING=$($AWSCLI apigateway get-stages $PROFILE_OPT --region $regx --rest-api-id $apigwid --query "item[?stageName == \`$stagname\` ].methodSettings" --output text|awk '{ print $1" log level "$6}') + if [[ $CHECK_STAGE_METHOD_LOGGING ]];then + textOK "$regx: API Gateway $API_GW_NAME has stage logging enabled for $CHECK_STAGE_METHOD_LOGGING" "$regx" + else + textWarn "$regx: API Gateway $API_GW_NAME logging disabled for stage $stagname!" "$regx" + fi + done + else + textWarn "$regx: No Stage name found for $API_GW_NAME" "$regx" + fi + done + else + textNotice "$regx: No API Gateway found" "$regx" + fi + done +} + callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in @@ -2113,16 +2219,20 @@ callCheck(){ extra77|extra707 ) extra77;; extra78|extra708 ) extra78;; extra79|extra709 ) extra79;; - extra710|extra710 ) extra710;; - extra711|extra711 ) extra711;; - extra712|extra712 ) extra712;; - extra713|extra713 ) extra713;; - extra714|extra714 ) extra714;; - extra715|extra715 ) extra715;; - extra716|extra716 ) extra716;; - extra717|extra717 ) extra717;; - extra718|extra718 ) extra718;; - extra719|extra719 ) extra719;; + extra710 ) extra710;; + extra711 ) extra711;; + extra712 ) extra712;; + extra713 ) extra713;; + extra714 ) extra714;; + extra715 ) extra715;; + extra716 ) extra716;; + extra717 ) extra717;; + extra718 ) extra718;; + extra719 ) extra719;; + extra720 ) extra720;; + extra721 ) extra721;; + extra722 ) extra722;; + ## Groups of Checks check1 ) @@ -2160,12 +2270,13 @@ callCheck(){ extras ) extra71;extra72;extra73;extra74;extra75;extra76;extra77;extra78; extra79;extra710;extra711;extra712;extra713;extra714;extra715;extra716; - extra717;extra718;extra719 + extra717;extra718;extra719;extra720;extra721;extra722 ;; forensics-ready ) check21;check22;check23;check24;check25;check26;check27; check43; - extra712;extra713;extra714;extra715;extra717;extra718;extra719 + extra712;extra713;extra714;extra715;extra717;extra718;extra719; + extra720;extra721;extra722 ;; * ) textWarn "ERROR! Use a valid check name (i.e. check41 or extra71)\n"; @@ -2255,6 +2366,9 @@ if [[ $PRINTCHECKSONLY == "1" ]]; then textTitle "$ID717" "$TITLE717" "NOT_SCORED" "EXTRA" textTitle "$ID718" "$TITLE718" "NOT_SCORED" "EXTRA" textTitle "$ID719" "$TITLE719" "NOT_SCORED" "EXTRA" + textTitle "$ID720" "$TITLE720" "NOT_SCORED" "EXTRA" + textTitle "$ID721" "$TITLE721" "NOT_SCORED" "EXTRA" + textTitle "$ID722" "$TITLE722" "NOT_SCORED" "EXTRA" exit $EXITCODE fi @@ -2330,26 +2444,31 @@ check43 check44 check45 -textTitle "7" "$TITLE7" "NOT_SCORED" "SUPPORT" -extra71 -extra72 -extra73 -extra74 -extra75 -extra76 -extra77 -extra78 -extra79 -extra710 -extra711 -extra712 -extra713 -extra714 -extra715 -extra716 -extra717 -extra718 -extra719 +if [[ ! $EXTRAS ]]; then + textTitle "7" "$TITLE7" "NOT_SCORED" "SUPPORT" + extra71 + extra72 + extra73 + extra74 + extra75 + extra76 + extra77 + extra78 + extra79 + extra710 + extra711 + extra712 + extra713 + extra714 + extra715 + extra716 + extra717 + extra718 + extra719 + extra720 + extra721 + extra722 +fi cleanTemp exit $EXITCODE diff --git a/prowler-policy-additions.json b/prowler-policy-additions.json index 9104c6da..66ba909c 100644 --- a/prowler-policy-additions.json +++ b/prowler-policy-additions.json @@ -9,7 +9,8 @@ "cloudwatchlogs:describemetricfilters", "es:describeelasticsearchdomainconfig", "ses:getidentityverificationattributes", - "sns:listsubscriptionsbytopic" + "sns:listsubscriptionsbytopic", + "guardduty:ListDetectors" ], "Effect": "Allow", "Resource": "*" From 65c417a357b3ac74121cc423154320e8cd642b7e Mon Sep 17 00:00:00 2001 From: Subramani Ramanathan Date: Wed, 21 Feb 2018 02:48:20 +0530 Subject: [PATCH 57/61] Improving check41 and check42 Improved check41 and check42 to ensure no inbound rule exists that has:- # port no 22 and source of 0.0.0.0/0 # port no in the range (i.e 0-1024) and source of 0.0.0.0/0 # port value of all and source of 0.0.0.0/0 --- prowler | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prowler b/prowler index d49581d6..ed335e57 100755 --- a/prowler +++ b/prowler @@ -1576,7 +1576,7 @@ check41(){ # "Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored)" textTitle "$ID41" "$TITLE41" "SCORED" "LEVEL1" for regx in $REGIONS; do - SG_LIST=$($AWSCLI ec2 describe-security-groups --filters "Name=ip-permission.to-port,Values=22" --query 'SecurityGroups[?length(IpPermissions[?ToPort==`22` && contains(IpRanges[].CidrIp, `0.0.0.0/0`)]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text) + SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`22` && ToPort>=`22`)) && contains(IpRanges[].CidrIp, `0.0.0.0/0`)]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text) if [[ $SG_LIST ]];then for SG in $SG_LIST;do textWarn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx" "$regx" @@ -1591,7 +1591,7 @@ check42(){ # "Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored)" textTitle "$ID42" "$TITLE42" "SCORED" "LEVEL1" for regx in $REGIONS; do - SG_LIST=$($AWSCLI ec2 describe-security-groups --filters "Name=ip-permission.to-port,Values=3389" --query 'SecurityGroups[?length(IpPermissions[?ToPort==`3389` && contains(IpRanges[].CidrIp, `0.0.0.0/0`)]) > `0`].{GroupName: GroupName}' $PROFILE_OPT --region $regx --output text) + SG_LIST=$($AWSCLI ec2 describe-security-groups --query 'SecurityGroups[?length(IpPermissions[?((FromPort==null && ToPort==null) || (FromPort<=`3389` && ToPort>=`3389`)) && contains(IpRanges[].CidrIp, `0.0.0.0/0`)]) > `0`].{GroupId:GroupId}' $PROFILE_OPT --region $regx --output text) if [[ $SG_LIST ]];then for SG in $SG_LIST;do textWarn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx" "$regx" From 1295c5ecff3c5b33eda4eac2fe3ae212b461aff2 Mon Sep 17 00:00:00 2001 From: Tanner Doshier Date: Thu, 22 Feb 2018 12:16:12 -0600 Subject: [PATCH 58/61] Use `#!/usr/bin/env bash` instead of hard coding `#!/bin/bash` Better cross-platform support. --- prowler | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler b/prowler index d49581d6..db3910d4 100755 --- a/prowler +++ b/prowler @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Prowler is a tool that provides automate auditing and hardening guidance of an AWS account. # It is based on AWS-CLI commands. It follows guidelines present in the CIS Amazon From d7f4f99f154045af8f9b691d941d76a2507fb8e2 Mon Sep 17 00:00:00 2001 From: Tanner Doshier Date: Thu, 22 Feb 2018 12:14:00 -0600 Subject: [PATCH 59/61] Improve check28 The CIS benchmarks state that only customer managed CMKs should be checked, so exclude all AWS managed CMKs, not just the one for ACM. Also fix up some formatting and dead code. --- prowler | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/prowler b/prowler index d49581d6..1b6145cf 100755 --- a/prowler +++ b/prowler @@ -1153,26 +1153,23 @@ check28(){ for regx in $REGIONS; do CHECK_KMS_KEYLIST=$($AWSCLI kms list-keys $PROFILE_OPT --region $regx --output text --query 'Keys[*].KeyId') if [[ $CHECK_KMS_KEYLIST ]];then - CHECK_KMS_KEYLIST_NO_DEFAULT=$(for key in $CHECK_KMS_KEYLIST ; do $AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --output text|grep -v 'Default master key that protects my ACM private keys when no other key is defined'|awk '{ print $3 }'|awk -F'/' '{ print $2 }'; done) + CHECK_KMS_KEYLIST_NO_DEFAULT=$(for key in $CHECK_KMS_KEYLIST ; do $AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --output text --query 'KeyMetadata.[KeyId, KeyManager]'|grep -v 'AWS'|awk '{ print $1 }'; done) for key in $CHECK_KMS_KEYLIST_NO_DEFAULT; do CHECK_KMS_KEY_TYPE=$($AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --query 'KeyMetadata.Origin' | sed 's/["]//g') if [[ "$CHECK_KMS_KEY_TYPE" == "EXTERNAL" ]];then textOK "Key $key in Region $regx Customer Uploaded Key Material." "$regx" else CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key $PROFILE_OPT --region $regx --output text) - #CHECK_KMS_DEFAULT_KEY=$($AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --query 'KeyMetadata.Description' | sed -n '/Default master key that protects my ACM private keys when no other key is defined /p'|| echo "False") - if [[ "$CHECK_KMS_KEY_ROTATION" == "True" ]];then - textOK "Key $key in Region $regx is set correctly" - elif [[ "$CHECK_KMS_KEY_ROTATION" == "False" && $CHECK_KMS_DEFAULT_KEY ]];then - textNotice "Region $regx key $key is an AWS default master key and cannot be deleted nor modified." "$regx" - else - textWarn "Key $key in Region $regx is not set to rotate!!!" "$regx" - fi + if [[ "$CHECK_KMS_KEY_ROTATION" == "True" ]];then + textOK "Key $key in Region $regx is set correctly" + else + textWarn "Key $key in Region $regx is not set to rotate!!!" "$regx" + fi fi done else - textNotice "Region $regx doesn't have encryption keys" "$regx" + textNotice "Region $regx doesn't have encryption keys" "$regx" fi done } From c20f8878daaefeb25e90e3048008983a954c0957 Mon Sep 17 00:00:00 2001 From: Jake Billo Date: Mon, 5 Mar 2018 16:01:42 -0500 Subject: [PATCH 60/61] Update IAM permissions needed for CloudWatch Logs According to the AWS documentation for the CloudWatch Logs permissions reference [1], the IAM policy to permit or deny CloudWatch Logs actions uses the `logs:` prefix rather than `cloudwatchlogs:`. This commit updates the policy additions JSON file as well as the README to reflect this change. I confirmed this having assumed an appropriate role in an AWS account, then executing the AWS CLI command `aws logs describe-log-groups`; with the `cloudwatchlogs:` prefix an AccessDeniedException was returned to the client. [1] https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/permissions-reference-cwl.html --- README.md | 9 ++++----- prowler-policy-additions.json | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0121f8fa..f33dad90 100644 --- a/README.md +++ b/README.md @@ -186,8 +186,6 @@ Instead of using default policy SecurityAudit for the account you use for checks "cloudtrail:gettrailstatus", "cloudtrail:listtags", "cloudwatch:describe*", - "cloudwatchlogs:describeloggroups", - "cloudwatchlogs:describemetricfilters", "codecommit:batchgetrepositories", "codecommit:getbranch", "codecommit:getobjectidentifier", @@ -229,7 +227,8 @@ Instead of using default policy SecurityAudit for the account you use for checks "kms:list*", "lambda:getpolicy", "lambda:listfunctions", - "logs:DescribeMetricFilters", + "logs:DescribeLogGroups", + "logs:DescribeMetricFilters", "rds:describe*", "rds:downloaddblogfileportion", "rds:listtagsforresource", @@ -294,9 +293,9 @@ Alternatively, here is a policy which defines the permissions which are NOT pres "Action": [ "acm:DescribeCertificate", "acm:ListCertificates", - "cloudwatchlogs:describeLogGroups", - "cloudwatchlogs:DescribeMetricFilters", "es:DescribeElasticsearchDomainConfig", + "logs:DescribeLogGroups", + "logs:DescribeMetricFilters", "ses:GetIdentityVerificationAttributes", "sns:ListSubscriptionsByTopic" ], diff --git a/prowler-policy-additions.json b/prowler-policy-additions.json index 66ba909c..0732fd84 100644 --- a/prowler-policy-additions.json +++ b/prowler-policy-additions.json @@ -5,9 +5,9 @@ "Action": [ "acm:describecertificate", "acm:listcertificates", - "cloudwatchlogs:describeloggroups", - "cloudwatchlogs:describemetricfilters", "es:describeelasticsearchdomainconfig", + "logs:DescribeLogGroups", + "logs:DescribeMetricFilters", "ses:getidentityverificationattributes", "sns:listsubscriptionsbytopic", "guardduty:ListDetectors" From 5156376df6750864850a1911c15101f76e050d21 Mon Sep 17 00:00:00 2001 From: Dan Borges Date: Tue, 6 Mar 2018 09:58:12 -0800 Subject: [PATCH 61/61] Update prowler --- prowler | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prowler b/prowler index c3dbee7f..fffc0d8c 100755 --- a/prowler +++ b/prowler @@ -2005,7 +2005,7 @@ extra717(){ if [[ $CHECK_ELBS_LOG_ENABLED ]]; then textOK "$regx: $elb has access logs to S3 configured" "$regx" else - textWarn "$regx: $elb has not access logs configured" "$regx" + textWarn "$regx: $elb has not configured access logs" "$regx" fi done fi @@ -2016,7 +2016,7 @@ extra717(){ if [[ $CHECK_ELBSV2_LOG_ENABLED ]]; then textOK "$regx: $ELBV2_NAME has access logs to S3 configured" "$regx" else - textWarn "$regx: $ELBV2_NAME has not access logs configured" "$regx" + textWarn "$regx: $ELBV2_NAME has not configured access logs" "$regx" fi done fi