From 045e88038f22b3285a3cbf2443dc6b8abf545be8 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Thu, 22 Jun 2017 16:16:44 -0500 Subject: [PATCH 01/22] Clean up ordering; add Mode flag refactor callCheck case statement to move cleanTemp && exit to end move calls to functions to after all functions are defined add -M flag to handle mode changing: text, mono, csv --- prowler | 165 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 100 insertions(+), 65 deletions(-) diff --git a/prowler b/prowler index 4fee7c00..4a217361 100755 --- a/prowler +++ b/prowler @@ -35,6 +35,7 @@ REGION="us-east-1" FILTERREGION="" MAXITEMS=100 MONOCHROME=0 +MODE="text" # Command usage menu usage(){ @@ -48,12 +49,13 @@ USAGE: -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) -b do not use colors in the output + -M output mode: text (defalut), mono, csv -h this help " exit } -while getopts ":hbp:r:c:f:m:" OPTION; do +while getopts ":hbp:r:c:f:m:M:" OPTION; do case $OPTION in h ) usage @@ -77,6 +79,9 @@ while getopts ":hbp:r:c:f:m:" OPTION; do m ) MAXITEMS=$OPTARG ;; + M ) + MODE=$OPTARG + ;; : ) echo "" echo "$OPTRED ERROR!$OPTNORMAL -$OPTARG requires an argument" @@ -92,6 +97,17 @@ while getopts ":hbp:r:c:f:m:" OPTION; do esac done +if [[ $MODE != "mono" && $MODE != "text" && $MODE != "csv" ]]; then + echo "" + echo "$OPTRED ERROR!$OPTNORMAL Invalid output mode. Choose text, mono, or csv." + usage + exit 1 +fi + +if [[ $MODE == "mono" ]]; then + MONOCHROME=1 +fi + if [[ $MONOCHROME -eq 1 ]]; then # Colors NORMAL='' @@ -300,13 +316,6 @@ infoReferenceShort(){ echo -e " $NOTICE http://bit.ly/2g3PEf7$NORMAL" } -prowlerBanner -printCurrentDate -getWhoami -printColorsCode -genCredReport -saveReport - check11(){ TITLE11="$BLUE 1.1$NORMAL 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): " @@ -1159,67 +1168,93 @@ check45(){ callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in - check11) check11;cleanTemp;exit;; - check12) check12;cleanTemp;exit;; - check13) check13;cleanTemp;exit;; - check14) check14;cleanTemp;exit;; - check15) check15;cleanTemp;exit;; - check16) check16;cleanTemp;exit;; - check17) check17;cleanTemp;exit;; - check18) check18;cleanTemp;exit;; - check19) check19;cleanTemp;exit;; - check110) check110;cleanTemp;exit;; - check111) check111;cleanTemp;exit;; - check112) check112;cleanTemp;exit;; - check113) check113;cleanTemp;exit;; - check114) check114;cleanTemp;exit;; - check115) check115;cleanTemp;exit;; - check116) check116;cleanTemp;exit;; - check117) check117;cleanTemp;exit;; - check118) check118;cleanTemp;exit;; - check119) check119;cleanTemp;exit;; - check120) check120;cleanTemp;exit;; - check121) check121;cleanTemp;exit;; - check122) check122;cleanTemp;exit;; - check123) check123;cleanTemp;exit;; - check124) check124;cleanTemp;exit;; - check21) check21;cleanTemp;exit;; - check22) check22;cleanTemp;exit;; - check23) check23;cleanTemp;exit;; - check24) check24;cleanTemp;exit;; - check25) check25;cleanTemp;exit;; - check26) check26;cleanTemp;exit;; - check27) check27;cleanTemp;exit;; - check28) check28;cleanTemp;exit;; - check31) check31;cleanTemp;exit;; - check32) check32;cleanTemp;exit;; - check33) check33;cleanTemp;exit;; - check34) check34;cleanTemp;exit;; - check35) check35;cleanTemp;exit;; - check36) check36;cleanTemp;exit;; - check37) check37;cleanTemp;exit;; - check38) check38;cleanTemp;exit;; - check39) check39;cleanTemp;exit;; - check310) check310;cleanTemp;exit;; - check311) check311;cleanTemp;exit;; - check312) check312;cleanTemp;exit;; - check313) check313;cleanTemp;exit;; - check314) check314;cleanTemp;exit;; - check315) check315;cleanTemp;exit;; - check41) check41;cleanTemp;exit;; - check42) check42;cleanTemp;exit;; - check43) check43;cleanTemp;exit;; - check44) check44;cleanTemp;exit;; - check45) check45;cleanTemp;exit;; - check1) check11;check12;check13;check14;check15;check16;check17;check18;check19;check110;check111;check112;check113;check114;check115;check116;check117;check118;check119;check120;check121;check122;check123;check124;cleanTemp;exit;; - check2) check21;check22;check23;check24;check25;check26;check27;check28;cleanTemp;exit;; - check3) check31;check32;check33;check34;check35;check36;check37;check38;check39;check310;check311;check312;check313;check314;check315;cleanTemp;exit;; - check4) check41;check42;check43;check44;check45;cleanTemp;exit;; - * ) echo -e "\n$RED ERROR! Use a valid check name (i.e. check41) $NORMAL\n";exit;; + check11) check11;; + check12) check12;; + check13) check13;; + check14) check14;; + check15) check15;; + check16) check16;; + check17) check17;; + check18) check18;; + check19) check19;; + check110) check110;; + check111) check111;; + check112) check112;; + check113) check113;; + check114) check114;; + check115) check115;; + check116) check116;; + check117) check117;; + check118) check118;; + check119) check119;; + check120) check120;; + check121) check121;; + check122) check122;; + check123) check123;; + check124) check124;; + check21) check21;; + check22) check22;; + check23) check23;; + check24) check24;; + check25) check25;; + check26) check26;; + check27) check27;; + check28) check28;; + check31) check31;; + check32) check32;; + check33) check33;; + check34) check34;; + check35) check35;; + check36) check36;; + check37) check37;; + check38) check38;; + check39) check39;; + check310) check310;; + check311) check311;; + check312) check312;; + check313) check313;; + check314) check314;; + check315) check315;; + check41) check41;; + check42) check42;; + check43) check43;; + check44) check44;; + check45) check45;; + check1) + check11;check12;check13;check14;check15;check16;check17;check18; + check19;check110;check111;check112;check113;check114;check115; + check116;check117;check118;check119;check120;check121;check122; + check123;check124; + ;; + check2) + check21;check22;check23;check24;check25;check26;check27;check28 + ;; + check3) + check31;check32;check33;check34;check35;check36;check37;check38; + check39;check310;check311;check312;check313;check314;check315 + ;; + check4) + check41;check42;check43;check44;check45 + ;; + * ) + echo -e "\n$RED ERROR! Use a valid check name (i.e. check41) $NORMAL\n"; esac + cleanTemp + exit fi } + +### All functions defined above ... run the workflow + +prowlerBanner +printCurrentDate +getWhoami +printColorsCode +genCredReport +saveReport + + callCheck TITLE1="$BLUE 1 Identity and Access Management *********************************$NORMAL" From 408cc7849957d7839677f4c9acfedcce92279c5c Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 10:26:56 -0500 Subject: [PATCH 02/22] begin transition to function-based output - starting with OK responses for check1* --- prowler | 98 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/prowler b/prowler index 4a217361..af3d7187 100755 --- a/prowler +++ b/prowler @@ -316,6 +316,10 @@ infoReferenceShort(){ echo -e " $NOTICE http://bit.ly/2g3PEf7$NORMAL" } +text_ok(){ + echo " $OK OK! $NORMAL $@" +} + check11(){ TITLE11="$BLUE 1.1$NORMAL 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): " @@ -337,7 +341,7 @@ check12(){ echo -e " List of users with Password enabled but MFA disabled:" echo -e " $BAD WARNING! $COMMAND12 $NORMAL" else - echo -e " $OK OK! $NORMAL No users found with Password enabled and MFA disabled" + text_ok " No users found with Password enabled and MFA disabled" fi } @@ -401,7 +405,7 @@ check15(){ COMMAND15=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireUppercaseCharacters') # must be true echo -e "\n$TITLE15 " if [[ $COMMAND15 == "true" ]];then - echo -e " $OK OK! $NORMAL" + text_ok "Password Policy requires upper case" else echo -e " $BAD WARNING! $NORMAL" fi @@ -412,7 +416,7 @@ check16(){ COMMAND16=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireLowercaseCharacters') # must be true echo -e "\n$TITLE16 " if [[ $COMMAND16 == "true" ]];then - echo -e " $OK OK! $NORMAL" + text_ok "Password Policy requires lower case" else echo -e " $BAD WARNING! $NORMAL" fi @@ -423,7 +427,7 @@ check17(){ COMMAND17=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireSymbols') # must be true echo -e "\n$TITLE17 " if [[ $COMMAND17 == "true" ]];then - echo -e " $OK OK! $NORMAL" + text_ok "Password Policy requires symbol" else echo -e " $BAD WARNING! $NORMAL" fi @@ -434,7 +438,7 @@ check18(){ COMMAND18=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireNumbers') # must be true echo -e "\n$TITLE18 " if [[ $COMMAND18 == "true" ]];then - echo -e " $OK OK! $NORMAL" + text_ok "Password Policy requires number" else echo -e " $BAD WARNING! $NORMAL" fi @@ -445,7 +449,7 @@ check19(){ COMMAND19=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.MinimumPasswordLength') echo -e "\n$TITLE19 " if [[ $COMMAND19 -gt "13" ]];then - echo -e " $OK OK! $NORMAL" + text_ok "Password Policy requires more than 13 characters" else echo -e " $BAD WARNING! $NORMAL" fi @@ -457,7 +461,7 @@ check110(){ echo -e "\n$TITLE110 " if [[ $COMMAND110 ]];then if [[ $COMMAND110 -gt "23" ]];then - echo -e " $OK OK! $NORMAL" + text_ok "Password Policy limits reuse" else echo -e " $BAD WARNING! It is not set or it is set lower than 24 $NORMAL" fi @@ -472,7 +476,7 @@ check111(){ echo -e "\n$TITLE111 " if [[ $COMMAND111 ]];then if [ $COMMAND111 == "90" ];then - echo -e " $OK OK! $NORMAL" + text_ok "Password Policy includes expiration" fi else echo -e " $BAD WARNING! Passowrd expiration not set or set greater than 90 days $NORMAL" @@ -486,12 +490,12 @@ check112(){ ROOTKEY2=$(cat $TEMP_REPORT_FILE |grep root_account|awk -F',' '{ print $14 }') echo -e "\n$TITLE112 " if [ $ROOTKEY1 == "false" ];then - echo -e " $OK OK! $NORMAL No access key 1 found for root " + text_ok "No access key 1 found for root" else echo -e " $BAD WARNING! Found access key 1 for root $NORMAL" fi if [ $ROOTKEY2 == "false" ];then - echo -e " $OK OK! $NORMAL No access key 2 found for root " + text_ok "No access key 2 found for root" else echo -e " $BAD WARNING! Found access key 2 for root $NORMAL" fi @@ -502,7 +506,7 @@ check113(){ COMMAND113=$($AWSCLI iam get-account-summary --profile $PROFILE --region $REGION |grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') echo -e "\n$TITLE113" if [ $COMMAND113 == "1" ]; then - echo " $OK OK! $NORMAL Virtual MFA is enabled. " + text_ok "Virtual MFA is enabled for root" else echo " $BAD WARNING! MFA is not ENABLED for root account $NORMAL" fi @@ -515,9 +519,9 @@ check114(){ if [ $COMMAND113 == "1" ]; then COMMAND114=$($AWSCLI iam list-virtual-mfa-devices --profile $PROFILE --region $REGION --query 'VirtualMFADevices' --output text|grep :root |wc -l) if [ $COMMAND114 == "1" ]; then - echo " $OK OK! $NORMAL Virtual MFA is enabled. " + text_ok "Virtual MFA is enabled for root" else - echo " $OK OK! $NORMAL Hardware MFA is enabled. " + text_ok "Hardware MFA is enabled for root " fi else echo " $BAD WARNING! MFA is not ENABLED for root account $NORMAL" @@ -547,7 +551,7 @@ check116(){ fi done if [[ $C116_NUM_USERS -eq 0 ]]; then - echo -e " $OK OK! $NORMAL No policies attached to users." + text_ok "No policies attached to users." fi } @@ -619,7 +623,7 @@ check122(){ for policyarn in $SUPPORTPOLICYARN;do POLICYTOSHOW=$($AWSCLI iam list-entities-for-policy --policy-arn $SUPPORTPOLICYARN --profile $PROFILE --region $REGION --output text) if [[ $POLICYTOSHOW ]];then - echo -e " $OK OK! $NORMAL $POLICYTOSHOW" + text_ok "Support Policy attached to $POLICYTOSHOW" echo -e " $NOTICE Make sure your team can create a Support case with AWS $NORMAL" else echo -e " $BAD WARNING! Support Policy not applied to any Group, User or Role $NORMAL" @@ -641,7 +645,7 @@ check123(){ echo -e " $NOTICE List of users with Access Key 1 never used:$NORMAL" echo -e " $NOTICE $LIST_USERS_KEY1_ACTIVE $NORMAL have never used Access Key 1" else - echo -e " $OK OK! $NORMAL No users found with Access Key 1 never used" + text_ok "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) @@ -650,7 +654,7 @@ check123(){ echo -e " $NOTICE List of users with Access Key 2 never used:$NORMAL" echo -e " $NOTICE $LIST_USERS_KEY2_ACTIVE $NORMAL have never used Access Key 2" else - echo -e " $OK OK! $NORMAL No users found with Access Key 2 never used" + text_ok "No users found with Access Key 2 never used" fi } @@ -673,10 +677,10 @@ check124(){ echo " $NOTICE Policy $policy allows \"*:*\" $NORMAL" done else - echo " $OK OK! $NORMAL No custom policy found that allow full \"*:*\" administrative privileges" + text_ok "No custom policy found that allow full \"*:*\" administrative privileges" fi else - echo " $OK OK! $NORMAL No custom policies found" + text_ok "No custom policies found" fi } @@ -690,7 +694,7 @@ check21(){ if [[ $MULTIREGION_TRAIL_STATUS == 'False' ]];then echo -e " $BAD WARNING! $trail trail in $REGION is not enabled in multi region mode$NORMAL" else - echo -e " $OK OK! $NORMAL $trail trail in $REGION is enabled for all regions" + text_ok " $trail trail in $REGION is enabled for all regions" fi done else @@ -708,7 +712,7 @@ check22(){ if [[ $LOGFILEVALIDATION_TRAIL_STATUS == 'False' ]];then echo -e " $BAD WARNING! $trail trail in $REGION has not log file validation enabled$NORMAL" else - echo -e " $OK OK! $NORMAL $trail trail in $REGION has log file validation enabled" + text_ok " $trail trail in $REGION has log file validation enabled" fi done else @@ -726,7 +730,7 @@ check23(){ if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]];then echo -e " $BAD WARNING! check your $bucket CloudTrail bucket ACL and Policy!$NORMAL" else - echo -e " $OK OK! $NORMAL Bucket $bucket is set correctly" + text_ok " Bucket $bucket is set correctly" fi done else @@ -751,7 +755,7 @@ check24(){ if [ $HOWOLDER -gt "1" ];then echo -e " $BAD WARNING! $trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)$NORMAL" else - echo -e " $OK OK! $NORMAL $trail trail has been logging during the last 24h (it is in $TRAIL_REGION)" + text_ok " $trail trail has been logging during the last 24h (it is in $TRAIL_REGION)" fi fi done @@ -766,7 +770,7 @@ check25(){ for regx in $REGIONS; do CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status --profile $PROFILE --region $regx | grep "recorder: ON") if [[ $CHECK_AWSCONFIG_STATUS ]];then - echo -e " $OK OK! $NORMAL Region $regx has AWS Config recorder: ON " + text_ok " Region $regx has AWS Config recorder: ON " else echo -e " $BAD WARNING! Region $regx has AWS Config disabled or not configured$NORMAL" fi @@ -781,7 +785,7 @@ check26(){ for bucket in $CLOUDTRAILBUCKET;do CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket --profile $PROFILE --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None) if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then - echo -e " $OK OK! $NORMAL It is enabled in $bucket" + text_ok " It is enabled in $bucket" else echo -e " $BAD WARNING! access logging is not enabled in $bucket CloudTrail S3 bucket!$NORMAL" fi @@ -799,7 +803,7 @@ check27(){ for trail in $CLOUDTRAILNAME;do CLOUDTRAILENC_ENABLED=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --trail $trail --query 'trailList[*].KmsKeyId' --output text) if [[ $CLOUDTRAILENC_ENABLED ]];then - echo -e " $OK OK! $NORMAL KMS key found for $trail" + text_ok " KMS key found for $trail" else echo -e " $BAD WARNING! encryption is not enabled in your CloudTrail trail $trail, KMS key not found!$NORMAL" fi @@ -819,12 +823,12 @@ check28(){ for key in $CHECK_KMS_KEYLIST_NO_DEFAULT; do CHECK_KMS_KEY_TYPE=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --region $regx --query 'KeyMetadata.Origin' | sed 's/["]//g') if [[ $CHECK_KMS_KEY_TYPE == "EXTERNAL" ]];then - echo -e " $OK OK! $NORMAL Key $key in Region $regx Customer Uploaded Key Material." + text_ok " Key $key in Region $regx Customer Uploaded Key Material." else CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key --profile $PROFILE --region $regx --output text) #CHECK_KMS_DEFAULT_KEY=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --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 - echo -e " $OK OK! $NORMAL Key $key in Region $regx is set correctly" + text_ok " Key $key in Region $regx is set correctly" elif [[ $CHECK_KMS_KEY_ROTATION == "False" && $CHECK_KMS_DEFAULT_KEY ]];then echo -e " $NOTICE Region $regx key $key is an AWS default master key and cannot be deleted nor modified.$NORMAL" else @@ -846,7 +850,7 @@ check31(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep AccessDenied) if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters for Access Denied enabled" + text_ok " CloudWatch group found, and metric filters for Access Denied enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -862,7 +866,7 @@ check32(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'userIdentity.sessionContext.attributes.mfaAuthenticated.*true') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters for sign-in Console without MFA enabled" + text_ok " CloudWatch group found, and metric filters for sign-in Console without MFA enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -878,7 +882,7 @@ check33(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters for usage of root account enabled" + text_ok " CloudWatch group found, and metric filters for usage of root account enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -894,7 +898,7 @@ check34(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --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 - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters for IAM policy changes enabled" + text_ok " CloudWatch group found, and metric filters for IAM policy changes enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -910,7 +914,7 @@ check35(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters for CloudTrail configuration changes enabled" + text_ok " CloudWatch group found, and metric filters for CloudTrail configuration changes enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -926,7 +930,7 @@ check36(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters for usage of root account enabled" + text_ok " CloudWatch group found, and metric filters for usage of root account enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -942,7 +946,7 @@ check37(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters enabled" + text_ok " CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -958,7 +962,7 @@ check38(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters enabled" + text_ok " CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -974,7 +978,7 @@ check39(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters enabled" + text_ok " CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -990,7 +994,7 @@ check310(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters enabled" + text_ok " CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1006,7 +1010,7 @@ check311(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters enabled" + text_ok " CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1022,7 +1026,7 @@ check312(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters enabled" + text_ok " CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1038,7 +1042,7 @@ check313(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters enabled" + text_ok " CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1054,7 +1058,7 @@ check314(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink') if [[ $METRICFILTER_SET ]];then - echo -e " $OK OK! $NORMAL CloudWatch group found, and metric filters enabled" + text_ok " CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1096,7 +1100,7 @@ check41(){ echo -e " $BAD WARNING! Found Security Group: $SG open to 0.0.0.0/0 in Region $regx $NORMAL " done else - echo -e " $OK OK! $NORMAL No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0 " + text_ok " No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0 " fi done } @@ -1111,7 +1115,7 @@ check42(){ echo -e " $BAD WARNING! Found Security Group: $SG open to 0.0.0.0/0 in Region $regx $NORMAL " done else - echo -e " $OK OK! $NORMAL No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0 " + text_ok " No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0 " fi done } @@ -1123,7 +1127,7 @@ check43(){ CHECK_FL=$($AWSCLI ec2 describe-flow-logs --profile $PROFILE --region $regx --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].LogGroupName' --output text) if [[ $CHECK_FL ]];then for FL in $CHECK_FL;do - echo -e " $OK OK! $NORMAL VPCFlowLog is enabled for LogGroupName: $FL in Region $regx " + text_ok " VPCFlowLog is enabled for LogGroupName: $FL in Region $regx " done else echo -e " $BAD WARNING! No VPCFlowLog has been found in Region $regx $NORMAL " @@ -1139,7 +1143,7 @@ check44(){ if [[ $CHECK_SGDEFAULT ]];then echo -e " $BAD WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region $regx $NORMAL " else - echo -e " $OK OK! $NORMAL No Default Security Groups open to 0.0.0.0 found in Region $regx " + text_ok " No Default Security Groups open to 0.0.0.0 found in Region $regx " fi done } @@ -1160,7 +1164,7 @@ check45(){ # done #echo $VPCS_WITH_PEERING else - echo -e " $OK OK! $NORMAL $regx: No VPC peering found " + text_ok " $regx: No VPC peering found " fi done } From 9bb8f0537a5cc33f04b386011bf18a244885ef22 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 10:30:33 -0500 Subject: [PATCH 03/22] finish up first pass of conversion to text_ok for passed checks --- prowler | 60 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/prowler b/prowler index af3d7187..63b97be9 100755 --- a/prowler +++ b/prowler @@ -364,12 +364,12 @@ check13(){ if [ $HOWOLDER -gt "90" ];then echo " $BAD WARNING! User \"$i\" has not logged in during the last 90 days $NORMAL" else - echo " $OK OK! $NORMAL User \"$i\" found with credentials used in the last 90 days" + echo text_ok "User \"$i\" found with credentials used in the last 90 days" fi done fi else - echo " $OK OK! $NORMAL No users found with password enabled" + text_ok "No users found with password enabled" fi } @@ -694,7 +694,7 @@ check21(){ if [[ $MULTIREGION_TRAIL_STATUS == 'False' ]];then echo -e " $BAD WARNING! $trail trail in $REGION is not enabled in multi region mode$NORMAL" else - text_ok " $trail trail in $REGION is enabled for all regions" + text_ok "$trail trail in $REGION is enabled for all regions" fi done else @@ -712,7 +712,7 @@ check22(){ if [[ $LOGFILEVALIDATION_TRAIL_STATUS == 'False' ]];then echo -e " $BAD WARNING! $trail trail in $REGION has not log file validation enabled$NORMAL" else - text_ok " $trail trail in $REGION has log file validation enabled" + text_ok "$trail trail in $REGION has log file validation enabled" fi done else @@ -730,7 +730,7 @@ check23(){ if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]];then echo -e " $BAD WARNING! check your $bucket CloudTrail bucket ACL and Policy!$NORMAL" else - text_ok " Bucket $bucket is set correctly" + text_ok "Bucket $bucket is set correctly" fi done else @@ -755,7 +755,7 @@ check24(){ if [ $HOWOLDER -gt "1" ];then echo -e " $BAD WARNING! $trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)$NORMAL" else - text_ok " $trail trail has been logging during the last 24h (it is in $TRAIL_REGION)" + text_ok "$trail trail has been logging during the last 24h (it is in $TRAIL_REGION)" fi fi done @@ -770,7 +770,7 @@ check25(){ for regx in $REGIONS; do CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status --profile $PROFILE --region $regx | grep "recorder: ON") if [[ $CHECK_AWSCONFIG_STATUS ]];then - text_ok " Region $regx has AWS Config recorder: ON " + text_ok "Region $regx has AWS Config recorder: ON " else echo -e " $BAD WARNING! Region $regx has AWS Config disabled or not configured$NORMAL" fi @@ -785,7 +785,7 @@ check26(){ for bucket in $CLOUDTRAILBUCKET;do CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket --profile $PROFILE --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None) if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then - text_ok " It is enabled in $bucket" + text_ok "Bucket access logging enabled in $bucket" else echo -e " $BAD WARNING! access logging is not enabled in $bucket CloudTrail S3 bucket!$NORMAL" fi @@ -803,7 +803,7 @@ check27(){ for trail in $CLOUDTRAILNAME;do CLOUDTRAILENC_ENABLED=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --trail $trail --query 'trailList[*].KmsKeyId' --output text) if [[ $CLOUDTRAILENC_ENABLED ]];then - text_ok " KMS key found for $trail" + text_ok "KMS key found for $trail" else echo -e " $BAD WARNING! encryption is not enabled in your CloudTrail trail $trail, KMS key not found!$NORMAL" fi @@ -823,12 +823,12 @@ check28(){ for key in $CHECK_KMS_KEYLIST_NO_DEFAULT; do CHECK_KMS_KEY_TYPE=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --region $regx --query 'KeyMetadata.Origin' | sed 's/["]//g') if [[ $CHECK_KMS_KEY_TYPE == "EXTERNAL" ]];then - text_ok " Key $key in Region $regx Customer Uploaded Key Material." + text_ok "Key $key in Region $regx Customer Uploaded Key Material." else CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key --profile $PROFILE --region $regx --output text) #CHECK_KMS_DEFAULT_KEY=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --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 - text_ok " Key $key in Region $regx is set correctly" + text_ok "Key $key in Region $regx is set correctly" elif [[ $CHECK_KMS_KEY_ROTATION == "False" && $CHECK_KMS_DEFAULT_KEY ]];then echo -e " $NOTICE Region $regx key $key is an AWS default master key and cannot be deleted nor modified.$NORMAL" else @@ -850,7 +850,7 @@ check31(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep AccessDenied) if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters for Access Denied enabled" + text_ok "CloudWatch group found, and metric filters for Access Denied enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -866,7 +866,7 @@ check32(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'userIdentity.sessionContext.attributes.mfaAuthenticated.*true') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters for sign-in Console without MFA enabled" + text_ok "CloudWatch group found, and metric filters for sign-in Console without MFA enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -882,7 +882,7 @@ check33(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters for usage of root account enabled" + text_ok "CloudWatch group found, and metric filters for usage of root account enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -898,7 +898,7 @@ check34(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --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 - text_ok " CloudWatch group found, and metric filters for IAM policy changes enabled" + text_ok "CloudWatch group found, and metric filters for IAM policy changes enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -914,7 +914,7 @@ check35(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters for CloudTrail configuration changes enabled" + text_ok "CloudWatch group found, and metric filters for CloudTrail configuration changes enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -930,7 +930,7 @@ check36(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters for usage of root account enabled" + text_ok "CloudWatch group found, and metric filters for usage of root account enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -946,7 +946,7 @@ check37(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters enabled" + text_ok "CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -962,7 +962,7 @@ check38(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters enabled" + text_ok "CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -978,7 +978,7 @@ check39(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters enabled" + text_ok "CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -994,7 +994,7 @@ check310(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters enabled" + text_ok "CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1010,7 +1010,7 @@ check311(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters enabled" + text_ok "CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1026,7 +1026,7 @@ check312(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters enabled" + text_ok "CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1042,7 +1042,7 @@ check313(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters enabled" + text_ok "CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1058,7 +1058,7 @@ check314(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink') if [[ $METRICFILTER_SET ]];then - text_ok " CloudWatch group found, and metric filters enabled" + text_ok "CloudWatch group found, and metric filters enabled" else echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" fi @@ -1100,7 +1100,7 @@ check41(){ echo -e " $BAD WARNING! Found Security Group: $SG open to 0.0.0.0/0 in Region $regx $NORMAL " done else - text_ok " No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0 " + text_ok "No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0 " fi done } @@ -1115,7 +1115,7 @@ check42(){ echo -e " $BAD WARNING! Found Security Group: $SG open to 0.0.0.0/0 in Region $regx $NORMAL " done else - text_ok " No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0 " + text_ok "No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0 " fi done } @@ -1127,7 +1127,7 @@ check43(){ CHECK_FL=$($AWSCLI ec2 describe-flow-logs --profile $PROFILE --region $regx --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].LogGroupName' --output text) if [[ $CHECK_FL ]];then for FL in $CHECK_FL;do - text_ok " VPCFlowLog is enabled for LogGroupName: $FL in Region $regx " + text_ok "VPCFlowLog is enabled for LogGroupName: $FL in Region $regx " done else echo -e " $BAD WARNING! No VPCFlowLog has been found in Region $regx $NORMAL " @@ -1143,7 +1143,7 @@ check44(){ if [[ $CHECK_SGDEFAULT ]];then echo -e " $BAD WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region $regx $NORMAL " else - text_ok " No Default Security Groups open to 0.0.0.0 found in Region $regx " + text_ok "No Default Security Groups open to 0.0.0.0 found in Region $regx " fi done } @@ -1164,7 +1164,7 @@ check45(){ # done #echo $VPCS_WITH_PEERING else - text_ok " $regx: No VPC peering found " + text_ok "$regx: No VPC peering found " fi done } From 029bce9b5b1798dba1acd2079851df3bc2d3d210 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 10:38:35 -0500 Subject: [PATCH 04/22] wrap all notice blocks to be single-line ready --- prowler | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/prowler b/prowler index 63b97be9..b8cb7059 100755 --- a/prowler +++ b/prowler @@ -532,9 +532,9 @@ check115(){ TITLE115="$BLUE 1.15$NORMAL Ensure security questions are registered in the AWS account (Not Scored)" # No command available echo -e "\n$TITLE115" - echo -e " $NOTICE No command available for check 1.14" - echo -e " Login to the AWS Console as root, click on the Account " - echo -e " Name -> My Account -> Configure Security Challenge Questions $NORMAL" + echo -e " $NOTICE No command available for check 1.15 $NORMAL" + echo -e " $NOTICE Login to the AWS Console as root, click on the Account $NORMAL" + echo -e " $NOTICE Name -> My Account -> Configure Security Challenge Questions $NORMAL" } check116(){ @@ -559,8 +559,8 @@ check117(){ TITLE117="$BLUE 1.17$NORMAL Enable detailed billing (Scored)" # No command available echo -e "\n$TITLE117 " - echo -e " $NOTICE No command available for check 1.17" - echo -e " See section 1.17 on the CIS Benchmark guide for details $NORMAL" + echo -e " $NOTICE No command available for check 1.17 $NORMAL" + echo -e " $NOTICE See section 1.17 on the CIS Benchmark guide for details $NORMAL" infoReferenceShort } @@ -593,8 +593,8 @@ check119(){ TITLE119="$BLUE 1.19$NORMAL Maintain current contact details (Scored)" # No command available echo -e "\n$TITLE119 " - echo -e " $NOTICE No command available for check 1.19" - echo -e " See section 1.19 on the CIS Benchmark guide for details $NORMAL" + echo -e " $NOTICE No command available for check 1.19 $NORMAL" + echo -e " $NOTICE See section 1.19 on the CIS Benchmark guide for details $NORMAL" infoReferenceShort } @@ -602,16 +602,16 @@ check120(){ TITLE120="$BLUE 1.20$NORMAL Ensure security contact information is registered (Scored)" # No command available echo -e "\n$TITLE120 " - echo -e " $NOTICE No command available for check 1.20" - echo -e " See section 1.20 on the CIS Benchmark guide for details $NORMAL" + echo -e " $NOTICE No command available for check 1.20 $NORMAL" + echo -e " $NOTICE See section 1.20 on the CIS Benchmark guide for details $NORMAL" infoReferenceShort } check121(){ TITLE121="$BLUE 1.21$NORMAL Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)" echo -e "\n$TITLE121 " - echo -e " $NOTICE No command available for check 1.21" - echo -e " See section 1.21 on the CIS Benchmark guide for details $NORMAL" + echo -e " $NOTICE No command available for check 1.21 $NORMAL" + echo -e " $NOTICE See section 1.21 on the CIS Benchmark guide for details $NORMAL" infoReferenceShort } From 3fadc1cce1cb63c75549f6c028097bbe7ab24b66 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 10:48:56 -0500 Subject: [PATCH 05/22] convert NOTICE text to use output function --- prowler | 82 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/prowler b/prowler index b8cb7059..5b52344e 100755 --- a/prowler +++ b/prowler @@ -311,21 +311,31 @@ infoReferenceLong(){ echo -e " $NOTICE https://github.com/Alfresco/aws-cis-security-benchmark/issues$NORMAL" } -infoReferenceShort(){ - # Report review note: - echo -e " $NOTICE http://bit.ly/2g3PEf7$NORMAL" -} + text_ok(){ echo " $OK OK! $NORMAL $@" } +text_notice(){ + echo " $NOTICE NOTICE! $@ $NORMAL" +} + +text_warn(){ + echo " $WARNING WARNING! $@ $NORMAL" +} + +infoReferenceShort(){ + # Report review note: + text_notice "http://bit.ly/2g3PEf7" +} + check11(){ TITLE11="$BLUE 1.1$NORMAL 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): " COMMAND11=$(cat $TEMP_REPORT_FILE| grep '' | cut -d, -f5,11,16 | sed 's/,/,\ /g') echo -e "\n$TITLE11" - echo -e " $NOTICE $COMMAND11 $NORMAL" + text_notice "$COMMAND11 $NORMAL" } check12(){ @@ -532,9 +542,9 @@ check115(){ TITLE115="$BLUE 1.15$NORMAL Ensure security questions are registered in the AWS account (Not Scored)" # No command available echo -e "\n$TITLE115" - echo -e " $NOTICE No command available for check 1.15 $NORMAL" - echo -e " $NOTICE Login to the AWS Console as root, click on the Account $NORMAL" - echo -e " $NOTICE Name -> My Account -> Configure Security Challenge Questions $NORMAL" + text_notice "No command available for check 1.15 " + text_notice "Login to the AWS Console as root, click on the Account " + text_notice "Name -> My Account -> Configure Security Challenge Questions " } check116(){ @@ -559,8 +569,8 @@ check117(){ TITLE117="$BLUE 1.17$NORMAL Enable detailed billing (Scored)" # No command available echo -e "\n$TITLE117 " - echo -e " $NOTICE No command available for check 1.17 $NORMAL" - echo -e " $NOTICE See section 1.17 on the CIS Benchmark guide for details $NORMAL" + text_notice "No command available for check 1.17 " + text_notice "See section 1.17 on the CIS Benchmark guide for details " infoReferenceShort } @@ -569,19 +579,19 @@ check118(){ echo -e "\n$TITLE118 " FINDMASTERANDMANAGER=$($AWSCLI iam list-roles --profile $PROFILE --region $REGION --query "Roles[*].{RoleName:RoleName}" --output text | grep -E 'Master|Manager'| tr '\n' ' ') if [[ $FINDMASTERANDMANAGER ]];then - echo -e " $NOTICE Found next roles as possible IAM Master and IAM Manager candidates: $NORMAL" - echo -e " $NOTICE $FINDMASTERANDMANAGER $NORMAL" - echo -e "\n $NOTICE INFO: run the commands below to check their policies with section 1.18 in the guide... $NORMAL" + text_notice "Found next roles as possible IAM Master and IAM Manager candidates: " + text_notice "$FINDMASTERANDMANAGER " + text_notice "run the commands below to check their policies with section 1.18 in the guide..." for role in $FINDMASTERANDMANAGER;do # find inline policies in found roles INLINEPOLICIES=$($AWSCLI iam list-role-policies --role-name $role --profile $PROFILE --region $REGION --query "PolicyNames[*]" --output text) for policy in $INLINEPOLICIES;do - echo " $NOTICE $AWSCLI iam get-role-policy --role-name $role --policy-name $policy --profile $PROFILE --region $REGION$NORMAL" + text_notice "$AWSCLI iam get-role-policy --role-name $role --policy-name $policy --profile $PROFILE --region $REGION" done # find attached policies in found roles ATTACHEDPOLICIES=$($AWSCLI iam list-attached-role-policies --role-name $role --profile $PROFILE --region $REGION --query "AttachedPolicies[*]" --output text) for policy in $ATTACHEDPOLICIES;do - echo " $NOTICE $AWSCLI iam get-role-policy --role-name $role --policy-name $policy --profile $PROFILE --region $REGION$NORMAL" + text-notice "$AWSCLI iam get-role-policy --role-name $role --policy-name $policy --profile $PROFILE --region $REGION" done done else @@ -593,8 +603,8 @@ check119(){ TITLE119="$BLUE 1.19$NORMAL Maintain current contact details (Scored)" # No command available echo -e "\n$TITLE119 " - echo -e " $NOTICE No command available for check 1.19 $NORMAL" - echo -e " $NOTICE See section 1.19 on the CIS Benchmark guide for details $NORMAL" + text_notice "No command available for check 1.19 " + text_notice "See section 1.19 on the CIS Benchmark guide for details " infoReferenceShort } @@ -602,16 +612,16 @@ check120(){ TITLE120="$BLUE 1.20$NORMAL Ensure security contact information is registered (Scored)" # No command available echo -e "\n$TITLE120 " - echo -e " $NOTICE No command available for check 1.20 $NORMAL" - echo -e " $NOTICE See section 1.20 on the CIS Benchmark guide for details $NORMAL" + text_notice "No command available for check 1.20 " + text_notice "See section 1.20 on the CIS Benchmark guide for details " infoReferenceShort } check121(){ TITLE121="$BLUE 1.21$NORMAL Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)" echo -e "\n$TITLE121 " - echo -e " $NOTICE No command available for check 1.21 $NORMAL" - echo -e " $NOTICE See section 1.21 on the CIS Benchmark guide for details $NORMAL" + text_notice "No command available for check 1.21 " + text_notice "See section 1.21 on the CIS Benchmark guide for details " infoReferenceShort } @@ -624,7 +634,7 @@ check122(){ POLICYTOSHOW=$($AWSCLI iam list-entities-for-policy --policy-arn $SUPPORTPOLICYARN --profile $PROFILE --region $REGION --output text) if [[ $POLICYTOSHOW ]];then text_ok "Support Policy attached to $POLICYTOSHOW" - echo -e " $NOTICE Make sure your team can create a Support case with AWS $NORMAL" + text_notice "Make sure your team can create a Support case with AWS " else echo -e " $BAD WARNING! Support Policy not applied to any Group, User or Role $NORMAL" fi @@ -642,8 +652,8 @@ check123(){ 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 - echo -e " $NOTICE List of users with Access Key 1 never used:$NORMAL" - echo -e " $NOTICE $LIST_USERS_KEY1_ACTIVE $NORMAL have never used Access Key 1" + text_notice "List of users with Access Key 1 never used:" + text_notice "$LIST_USERS_KEY1_ACTIVE have never used Access Key 1" else text_ok "No users found with Access Key 1 never used" fi @@ -651,8 +661,8 @@ check123(){ 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 - echo -e " $NOTICE List of users with Access Key 2 never used:$NORMAL" - echo -e " $NOTICE $LIST_USERS_KEY2_ACTIVE $NORMAL have never used Access Key 2" + text_notice "List of users with Access Key 2 never used:" + text_notice "$LIST_USERS_KEY2_ACTIVE have never used Access Key 2" else text_ok "No users found with Access Key 2 never used" fi @@ -663,7 +673,7 @@ check124(){ echo -e "\n$TITLE124" LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text --profile $PROFILE --region $REGION|grep 'arn:aws:iam::[0-9]\{12\}:'|awk '{ print $2 }') if [[ $LIST_CUSTOM_POLICIES ]]; then - echo -e " $NOTICE Looking for custom policies: (skipping default policies, it may take few seconds...)$NORMAL" + text_notice "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 $PROFILE --region $REGION --query 'Policies[*].[Arn,DefaultVersionId]' --output text|grep -w $policy |awk '{ 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 $PROFILE --region $REGION) @@ -672,9 +682,9 @@ check124(){ fi done if [[ $POLICIES_ALLOW_LIST ]]; then - echo -e " $NOTICE List of custom policies: $NORMAL" + text_notice "List of custom policies: " for policy in $POLICIES_ALLOW_LIST; do - echo " $NOTICE Policy $policy allows \"*:*\" $NORMAL" + text_notice "Policy $policy allows \"*:*\"" done else text_ok "No custom policy found that allow full \"*:*\" administrative privileges" @@ -830,7 +840,7 @@ check28(){ if [[ $CHECK_KMS_KEY_ROTATION == "True" ]];then text_ok "Key $key in Region $regx is set correctly" elif [[ $CHECK_KMS_KEY_ROTATION == "False" && $CHECK_KMS_DEFAULT_KEY ]];then - echo -e " $NOTICE Region $regx key $key is an AWS default master key and cannot be deleted nor modified.$NORMAL" + text_notice "Region $regx key $key is an AWS default master key and cannot be deleted nor modified." else echo -e " $BAD WARNING! Key $key in Region $regx is not set to rotate!!!$NORMAL" fi @@ -838,7 +848,7 @@ check28(){ done else - echo -e " $NOTICE Region $regx doesn't have encryption keys $NORMAL" + text_notice "Region $regx doesn't have encryption keys " fi done } @@ -1077,15 +1087,15 @@ check315(){ CHECK_TOPIC_LIST=$($AWSCLI sns list-subscriptions-by-topic --topic-arn $topic --profile $PROFILE --region $regx --query 'Subscriptions[*].{Endpoint:Endpoint,Protocol:Protocol}' --output text --max-items $MAXITEMS | grep -v "None") if [[ $CHECK_TOPIC_LIST ]]; then TOPIC_SHORT=$(echo $topic | awk -F: '{ print $7 }') - echo -e " $NOTICE Region $regx with Topic $TOPIC_SHORT: $NORMAL " - echo -e " $NOTICE - Suscription: $CHECK_TOPIC_LIST $NORMAL" + text_notice "Region $regx with Topic $TOPIC_SHORT: " + text_notice "- Suscription: $CHECK_TOPIC_LIST " else echo -e " $BAD WARNING! No suscription found in: Region $regx and Topic $topic $NORMAL" echo -e " $BAD - Region $regx and Topic $topic $NORMAL" fi done else - echo -e " $NOTICE Region $regx doesn't have topics $NORMAL" + text_notice "Region $regx doesn't have topics " fi done } @@ -1152,11 +1162,11 @@ check45(){ #set -xe TITLE45="$BLUE 4.5$NORMAL Ensure routing tables for VPC peering are \"least access\" (Not Scored)" echo -e "\n$TITLE45 " - echo -e " $NOTICE Looking for VPC peering in all regions... $NORMAL " + text_notice "Looking for VPC peering in all regions... " for regx in $REGIONS; do LIST_OF_VPCS_PEERING_CONNECTIONS=$($AWSCLI ec2 describe-vpc-peering-connections --output text --profile $PROFILE --region $regx --query 'VpcPeeringConnections[*].VpcPeeringConnectionId') if [[ $LIST_OF_VPCS_PEERING_CONNECTIONS ]];then - echo -e " $NOTICE $regx: $LIST_OF_VPCS_PEERING_CONNECTIONS, review its routing tables $NORMAL " + text_notice "$regx: $LIST_OF_VPCS_PEERING_CONNECTIONS, review its routing tables " #LIST_OF_VPCS=$($AWSCLI ec2 describe-vpcs --profile $PROFILE --region $regx --query 'Vpcs[*].VpcId' --output text) #aws ec2 describe-route-tables --filter "Name=vpc-id,Values=vpc-0213e864" --query "RouteTables[*].{RouteTableId:RouteTableId, VpcId:VpcId, Routes:Routes, AssociatedSubnets:Associations[*].SubnetId}" --profile $PROFILE --region $regx # for vpc in $LIST_OF_VPCS; do From a3a23eb8dcae095207aeef1de4875fd8e2f0ffb0 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 10:54:12 -0500 Subject: [PATCH 06/22] move output formatting functions up in file; use them earlier --- prowler | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/prowler b/prowler index 5b52344e..241150ff 100755 --- a/prowler +++ b/prowler @@ -240,6 +240,20 @@ fi # AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} # AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY_ID} +## Output formatting functions +text_ok(){ + echo " $OK OK! $NORMAL $@" +} + +text_notice(){ + echo " $NOTICE NOTICE! $@ $NORMAL" +} + +text_warn(){ + echo " $WARNING WARNING! $@ $NORMAL" +} + + prowlerBanner() { echo -e "$CYAN _" echo -e " _ __ _ __ _____ _| | ___ _ __" @@ -265,8 +279,7 @@ getWhoami(){ } printCurrentDate(){ - echo "" - echo "Date: ${NOTICE}$(date)${NORMAL}" + text_notice "Date: $(date)" } printColorsCode(){ @@ -304,27 +317,13 @@ REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' \ infoReferenceLong(){ # Report review note: - echo -e " $BLUE \n*********************************$NORMAL" - echo -e " $NOTICE For more information: $NORMAL" - echo -e " $NOTICE https://benchmarks.cisecurity.org/tools2/amazon/CIS_Amazon_Web_Services_Foundations_Benchmark_v1.1.0.pdf$NORMAL" - echo -e " $NOTICE For bugs or feedback: $NORMAL" - echo -e " $NOTICE https://github.com/Alfresco/aws-cis-security-benchmark/issues$NORMAL" + text_notice "For more information:" + text_notice "https://benchmarks.cisecurity.org/tools2/amazon/CIS_Amazon_Web_Services_Foundations_Benchmark_v1.1.0.pdf" + text_notice "For bugs or feedback:" + text_notice "https://github.com/Alfresco/aws-cis-security-benchmark/issues" } - -text_ok(){ - echo " $OK OK! $NORMAL $@" -} - -text_notice(){ - echo " $NOTICE NOTICE! $@ $NORMAL" -} - -text_warn(){ - echo " $WARNING WARNING! $@ $NORMAL" -} - infoReferenceShort(){ # Report review note: text_notice "http://bit.ly/2g3PEf7" @@ -351,7 +350,7 @@ check12(){ echo -e " List of users with Password enabled but MFA disabled:" echo -e " $BAD WARNING! $COMMAND12 $NORMAL" else - text_ok " No users found with Password enabled and MFA disabled" + text_ok "No users found with Password enabled and MFA disabled" fi } From 169eb51b0dd125840ea5f8788ab5a1f412e235a7 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 13:09:46 -0500 Subject: [PATCH 07/22] switch WARNING output to use text_warn function --- prowler | 146 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/prowler b/prowler index 241150ff..7ccf34e8 100755 --- a/prowler +++ b/prowler @@ -250,7 +250,7 @@ text_notice(){ } text_warn(){ - echo " $WARNING WARNING! $@ $NORMAL" + echo " $BAD WARNING! $@ $NORMAL" } @@ -268,7 +268,7 @@ getWhoami(){ echo "" echo "This report is being generated using credentials below:" echo "" - echo -e "AWS-CLI Profile: $NOTICE[$PROFILE]$NORMAL AWS API Region: $NOTICE[$REGION]$NORMAL AWS Filter Region: $NOTICE[${FILTERREGION:-all}]\n" + echo -e "AWS-CLI Profile: $NOTICE[$PROFILE]$NORMAL AWS API Region: $NOTICE[$REGION]$NORMAL AWS Filter Region: $NOTICE[${FILTERREGION:-all}]$NORMAL\n" if [[ $MONOCHROME -eq 1 ]]; then $AWSCLI sts get-caller-identity --output json --profile $PROFILE --region $REGION | grep ':' else @@ -334,7 +334,7 @@ check11(){ (password last used, access_key_1_last_used, access_key_2_last_used): " COMMAND11=$(cat $TEMP_REPORT_FILE| grep '' | cut -d, -f5,11,16 | sed 's/,/,\ /g') echo -e "\n$TITLE11" - text_notice "$COMMAND11 $NORMAL" + text_notice "$COMMAND11" } check12(){ @@ -348,7 +348,7 @@ check12(){ echo -e "\n$TITLE12" if [[ $COMMAND12 ]]; then echo -e " List of users with Password enabled but MFA disabled:" - echo -e " $BAD WARNING! $COMMAND12 $NORMAL" + text_warn "$COMMAND12 " else text_ok "No users found with Password enabled and MFA disabled" fi @@ -371,9 +371,9 @@ check13(){ DATEUSED=$($AWSCLI iam list-users --query "Users[?UserName=='$i'].PasswordLastUsed" --output text --profile $PROFILE --region $REGION | cut -d'T' -f1) HOWOLDER=$(how_older_from_today $DATEUSED) if [ $HOWOLDER -gt "90" ];then - echo " $BAD WARNING! User \"$i\" has not logged in during the last 90 days $NORMAL" + text_warn "User \"$i\" has not logged in during the last 90 days " else - echo text_ok "User \"$i\" found with credentials used in the last 90 days" + text_ok "User \"$i\" found with credentials used in the last 90 days" fi done fi @@ -395,7 +395,7 @@ check14(){ HOWOLDER=$(how_older_from_today $DATEROTATED1) if [ $HOWOLDER -gt "90" ];then - echo -e " $BAD WARNING! $user has not rotated access key1. $NORMAL" + text_warn " $user has not rotated access key1. " fi done echo -e " Users with access key 2 older than 90 days: " @@ -404,7 +404,7 @@ check14(){ DATEROTATED2=$(cat $TEMP_REPORT_FILE | 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 - echo -e " $BAD WARNING! $user has not rotated access key2. $NORMAL" + text_warn " $user has not rotated access key2. " fi done } @@ -416,7 +416,7 @@ check15(){ if [[ $COMMAND15 == "true" ]];then text_ok "Password Policy requires upper case" else - echo -e " $BAD WARNING! $NORMAL" + text_warn "Password Policy missing upper-case requirement" fi } @@ -427,7 +427,7 @@ check16(){ if [[ $COMMAND16 == "true" ]];then text_ok "Password Policy requires lower case" else - echo -e " $BAD WARNING! $NORMAL" + text_warn "Password Policy missing lower-case requirement" fi } @@ -438,7 +438,7 @@ check17(){ if [[ $COMMAND17 == "true" ]];then text_ok "Password Policy requires symbol" else - echo -e " $BAD WARNING! $NORMAL" + text_warn "Password Policy missing symbol requirement" fi } @@ -449,7 +449,7 @@ check18(){ if [[ $COMMAND18 == "true" ]];then text_ok "Password Policy requires number" else - echo -e " $BAD WARNING! $NORMAL" + text_warn "Password Policy missing number requirement" fi } @@ -460,7 +460,7 @@ check19(){ if [[ $COMMAND19 -gt "13" ]];then text_ok "Password Policy requires more than 13 characters" else - echo -e " $BAD WARNING! $NORMAL" + text_warn "Password Policy missing or weak length requirement" fi } @@ -472,10 +472,10 @@ check110(){ if [[ $COMMAND110 -gt "23" ]];then text_ok "Password Policy limits reuse" else - echo -e " $BAD WARNING! It is not set or it is set lower than 24 $NORMAL" + text_warn "Password Policy has weak reuse requirment (lower than 24)" fi else - echo -e " $BAD WARNING! It is not set $NORMAL" + text_warn "Password Policy missing reuse requirement" fi } @@ -488,7 +488,7 @@ check111(){ text_ok "Password Policy includes expiration" fi else - echo -e " $BAD WARNING! Passowrd expiration not set or set greater than 90 days $NORMAL" + text_warn "Passowrd expiration not set or set greater than 90 days " fi } @@ -501,12 +501,12 @@ check112(){ if [ $ROOTKEY1 == "false" ];then text_ok "No access key 1 found for root" else - echo -e " $BAD WARNING! Found access key 1 for root $NORMAL" + text_warn "Found access key 1 for root " fi if [ $ROOTKEY2 == "false" ];then text_ok "No access key 2 found for root" else - echo -e " $BAD WARNING! Found access key 2 for root $NORMAL" + text_warn "Found access key 2 for root " fi } @@ -517,7 +517,7 @@ check113(){ if [ $COMMAND113 == "1" ]; then text_ok "Virtual MFA is enabled for root" else - echo " $BAD WARNING! MFA is not ENABLED for root account $NORMAL" + text_warn "MFA is not ENABLED for root account " fi } @@ -533,7 +533,7 @@ check114(){ text_ok "Hardware MFA is enabled for root " fi else - echo " $BAD WARNING! MFA is not ENABLED for root account $NORMAL" + text_warn "MFA is not ENABLED for root account " fi } @@ -555,7 +555,7 @@ check116(){ for user in $LIST_USERS;do USER_POLICY=$($AWSCLI iam list-attached-user-policies --output text --profile $PROFILE --region $REGION --user-name $user) if [[ $USER_POLICY ]]; then - echo -e " $BAD WARNING! $user has policy directly attached $NORMAL" + text_warn "$user has policy directly attached " C116_NUM_USERS=$(expr $C116_NUM_USERS + 1) fi done @@ -594,7 +594,7 @@ check118(){ done done else - echo -e " $BAD WARNING! IAM Master and IAM Manager roles not found$NORMAL" + text_warn "IAM Master and IAM Manager roles not found" fi } @@ -635,11 +635,11 @@ check122(){ text_ok "Support Policy attached to $POLICYTOSHOW" text_notice "Make sure your team can create a Support case with AWS " else - echo -e " $BAD WARNING! Support Policy not applied to any Group, User or Role $NORMAL" + text_warn "Support Policy not applied to any Group, User or Role " fi done else - echo -e " $BAD WARNING! No Support Policy found$NORMAL" + text_warn "No Support Policy found" fi } @@ -701,13 +701,13 @@ check21(){ for trail in $LIST_OF_TRAILS;do MULTIREGION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].IsMultiRegionTrail' --output text --trail-name-list $trail) if [[ $MULTIREGION_TRAIL_STATUS == 'False' ]];then - echo -e " $BAD WARNING! $trail trail in $REGION is not enabled in multi region mode$NORMAL" + text_warn "$trail trail in $REGION is not enabled in multi region mode" else text_ok "$trail trail in $REGION is enabled for all regions" fi done else - echo -e " $BAD WARNING! No CloudTrail trails found!$NORMAL" + text_warn "No CloudTrail trails found!" fi } @@ -719,13 +719,13 @@ check22(){ for trail in $LIST_OF_TRAILS;do LOGFILEVALIDATION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].LogFileValidationEnabled' --output text --trail-name-list $trail) if [[ $LOGFILEVALIDATION_TRAIL_STATUS == 'False' ]];then - echo -e " $BAD WARNING! $trail trail in $REGION has not log file validation enabled$NORMAL" + text_warn "$trail trail in $REGION has not log file validation enabled" else text_ok "$trail trail in $REGION has log file validation enabled" fi done else - echo -e " $BAD WARNING! No CloudTrail trails found!$NORMAL" + text_warn "No CloudTrail trails found!" fi } @@ -737,13 +737,13 @@ check23(){ for bucket in $CLOUDTRAILBUCKET;do CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $bucket --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --profile $PROFILE --region $REGION --output text) if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]];then - echo -e " $BAD WARNING! check your $bucket CloudTrail bucket ACL and Policy!$NORMAL" + text_warn "check your $bucket CloudTrail bucket ACL and Policy!" else text_ok "Bucket $bucket is set correctly" fi done else - echo -e " $BAD WARNING! No CloudTrail bucket found!$NORMAL" + text_warn "No CloudTrail bucket found!" fi } @@ -757,19 +757,19 @@ check24(){ TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1) LATESTDELIVERY_TIMESTAMP=$($AWSCLI cloudtrail get-trail-status --name $trail --profile $PROFILE --region $TRAIL_REGION --query 'LatestCloudWatchLogsDeliveryTime' --output text|grep -v None) if [[ ! $LATESTDELIVERY_TIMESTAMP ]];then - echo -e " $BAD WARNING! $trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)$NORMAL" + text_warn "$trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)" else LATESTDELIVERY_DATE=$(timestamp_to_date $LATESTDELIVERY_TIMESTAMP) HOWOLDER=$(how_older_from_today $LATESTDELIVERY_DATE) if [ $HOWOLDER -gt "1" ];then - echo -e " $BAD WARNING! $trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)$NORMAL" + text_warn "$trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)" else text_ok "$trail trail has been logging during the last 24h (it is in $TRAIL_REGION)" fi fi done else - echo -e " $BAD WARNING! No CloudTrail trails found!$NORMAL" + text_warn "No CloudTrail trails found!" fi } @@ -781,7 +781,7 @@ check25(){ if [[ $CHECK_AWSCONFIG_STATUS ]];then text_ok "Region $regx has AWS Config recorder: ON " else - echo -e " $BAD WARNING! Region $regx has AWS Config disabled or not configured$NORMAL" + text_warn "Region $regx has AWS Config disabled or not configured" fi done } @@ -796,11 +796,11 @@ check26(){ if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then text_ok "Bucket access logging enabled in $bucket" else - echo -e " $BAD WARNING! access logging is not enabled in $bucket CloudTrail S3 bucket!$NORMAL" + text_warn "access logging is not enabled in $bucket CloudTrail S3 bucket!" fi done else - echo -e " $BAD WARNING! CloudTrail bucket not found!$NORMAL" + text_warn "CloudTrail bucket not found!" fi } @@ -814,11 +814,11 @@ check27(){ if [[ $CLOUDTRAILENC_ENABLED ]];then text_ok "KMS key found for $trail" else - echo -e " $BAD WARNING! encryption is not enabled in your CloudTrail trail $trail, KMS key not found!$NORMAL" + text_warn "encryption is not enabled in your CloudTrail trail $trail, KMS key not found!" fi done else - echo -e " $BAD WARNING! CloudTrail bucket doesn't exist!$NORMAL" + text_warn "CloudTrail bucket doesn't exist!" fi } @@ -841,7 +841,7 @@ check28(){ elif [[ $CHECK_KMS_KEY_ROTATION == "False" && $CHECK_KMS_DEFAULT_KEY ]];then text_notice "Region $regx key $key is an AWS default master key and cannot be deleted nor modified." else - echo -e " $BAD WARNING! Key $key in Region $regx is not set to rotate!!!$NORMAL" + text_warn "Key $key in Region $regx is not set to rotate!!!" fi fi done @@ -861,10 +861,10 @@ check31(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters for Access Denied enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -877,10 +877,10 @@ check32(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters for sign-in Console without MFA enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -893,10 +893,10 @@ check33(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters for usage of root account enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -909,10 +909,10 @@ check34(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters for IAM policy changes enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -925,10 +925,10 @@ check35(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters for CloudTrail configuration changes enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -941,10 +941,10 @@ check36(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters for usage of root account enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -957,10 +957,10 @@ check37(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -973,10 +973,10 @@ check38(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -989,10 +989,10 @@ check39(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -1005,10 +1005,10 @@ check310(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -1021,10 +1021,10 @@ check311(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -1037,10 +1037,10 @@ check312(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -1053,10 +1053,10 @@ check313(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -1069,10 +1069,10 @@ check314(){ if [[ $METRICFILTER_SET ]];then text_ok "CloudWatch group found, and metric filters enabled" else - echo -e " $BAD WARNING! CloudWatch group found, but no metric filters or alarms associated$NORMAL" + text_warn "CloudWatch group found, but no metric filters or alarms associated" fi else - echo -e " $BAD WARNING! No CloudWatch group found, no metric filters or alarms associated$NORMAL" + text_warn "No CloudWatch group found, no metric filters or alarms associated" fi } @@ -1089,8 +1089,8 @@ check315(){ text_notice "Region $regx with Topic $TOPIC_SHORT: " text_notice "- Suscription: $CHECK_TOPIC_LIST " else - echo -e " $BAD WARNING! No suscription found in: Region $regx and Topic $topic $NORMAL" - echo -e " $BAD - Region $regx and Topic $topic $NORMAL" + text_warn "No suscription found in: Region $regx and Topic $topic " + text_warn " - Region $regx and Topic $topic " fi done else @@ -1106,7 +1106,7 @@ check41(){ 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`].{GroupName: GroupName}' --profile $PROFILE --region $regx --output text) if [[ $SG_LIST ]];then for SG in $SG_LIST;do - echo -e " $BAD WARNING! Found Security Group: $SG open to 0.0.0.0/0 in Region $regx $NORMAL " + text_warn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx " done else text_ok "No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0 " @@ -1121,7 +1121,7 @@ check42(){ 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 $PROFILE --region $regx --output text) if [[ $SG_LIST ]];then for SG in $SG_LIST;do - echo -e " $BAD WARNING! Found Security Group: $SG open to 0.0.0.0/0 in Region $regx $NORMAL " + text_warn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx " done else text_ok "No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0 " @@ -1139,7 +1139,7 @@ check43(){ text_ok "VPCFlowLog is enabled for LogGroupName: $FL in Region $regx " done else - echo -e " $BAD WARNING! No VPCFlowLog has been found in Region $regx $NORMAL " + text_warn "No VPCFlowLog has been found in Region $regx " fi done } @@ -1150,7 +1150,7 @@ check44(){ for regx in $REGIONS; do CHECK_SGDEFAULT=$($AWSCLI ec2 describe-security-groups --profile $PROFILE --region $regx --filters Name=group-name,Values='default' --query 'SecurityGroups[*].{IpPermissions:IpPermissions,IpPermissionsEgress:IpPermissionsEgress,GroupId:GroupId}' --output text |grep 0.0.0.0) if [[ $CHECK_SGDEFAULT ]];then - echo -e " $BAD WARNING! Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region $regx $NORMAL " + text_warn "Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region $regx" else text_ok "No Default Security Groups open to 0.0.0.0 found in Region $regx " fi From f3be4f4c38b4550dfdd4e9ec8d9399e00d6d2315 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 13:24:45 -0500 Subject: [PATCH 08/22] cleaning up output messages, clean up logic on chec14 --- prowler | 56 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/prowler b/prowler index 7ccf34e8..827036dc 100755 --- a/prowler +++ b/prowler @@ -347,8 +347,7 @@ check12(){ done) echo -e "\n$TITLE12" if [[ $COMMAND12 ]]; then - echo -e " List of users with Password enabled but MFA disabled:" - text_warn "$COMMAND12 " + text_warn "Users with Password enabled by MFA disabled: $COMMAND12" else text_ok "No users found with Password enabled and MFA disabled" fi @@ -388,25 +387,45 @@ check14(){ 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 }') echo -e "\n$TITLE14 " - echo -e " 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 $user| awk -F, '{ print $10 }' | grep -v "N/A" | awk -F"T" '{ print $1 }') - HOWOLDER=$(how_older_from_today $DATEROTATED1) + C14_NUM_USERS1=0 + C14_NUM_USERS2=0 + # $(expr $C116_NUM_USERS + 1) + if [[ $LIST_OF_USERS_WITH_ACCESS_KEY1 ]]; then + # text_warn "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 $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 - text_warn " $user has not rotated access key1. " + if [ $HOWOLDER -gt "90" ];then + text_warn " $user has not rotated access key1 in over 90 days " + C14_NUM_USERS1 = $(expr $C14_NUM_USERS1 + 1) + fi + done + if [[ $C14_NUM_USERS1 -eq 0 ]]; then + text_ok "No users with access key 1 older than 90 days." fi - done - echo -e " 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 $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 - text_warn " $user has not rotated access key2. " + else + text_ok "No users with access key 1." + fi + + if [[ $LIST_OF_USERS_WITH_ACCESS_KEY2 ]]; then + # text_warn "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 $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 + text_warn " $user has not rotated access key2. " + C14_NUM_USERS2 = $(expr $C14_NUM_USERS2 + 1) + fi + done + if [[ $C14_NUM_USERS2 -eq 0 ]]; then + text_ok "No users with access key 2 older than 90 days." fi - done + else + text_ok "No users with access key 2." + fi } check15(){ @@ -550,7 +569,6 @@ check116(){ TITLE116="$BLUE 1.16$NORMAL Ensure IAM policies are attached only to groups or roles (Scored)" echo -e "\n$TITLE116" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text --profile $PROFILE --region $REGION) - echo -e " Users with policy attached to them instead to groups: (it may take few seconds...) " C116_NUM_USERS=0 for user in $LIST_USERS;do USER_POLICY=$($AWSCLI iam list-attached-user-policies --output text --profile $PROFILE --region $REGION --user-name $user) From 57034ab47dc922ffa05e2e0a8b5231dd638daa98 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 13:42:00 -0500 Subject: [PATCH 09/22] move titles from echo to output function --- prowler | 248 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 126 insertions(+), 122 deletions(-) diff --git a/prowler b/prowler index 827036dc..72555387 100755 --- a/prowler +++ b/prowler @@ -253,6 +253,9 @@ text_warn(){ echo " $BAD WARNING! $@ $NORMAL" } +text_title(){ + echo -e "\n$@" +} prowlerBanner() { echo -e "$CYAN _" @@ -330,11 +333,10 @@ infoReferenceShort(){ } check11(){ - TITLE11="$BLUE 1.1$NORMAL 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): " + TITLE11="$BLUE 1.1$NORMAL Avoid the use of the root account (Scored)." COMMAND11=$(cat $TEMP_REPORT_FILE| grep '' | cut -d, -f5,11,16 | sed 's/,/,\ /g') - echo -e "\n$TITLE11" - text_notice "$COMMAND11" + text_title "$TITLE11" + text_notice "Root account last accessed (password, key_1, key_2): $COMMAND11" } check12(){ @@ -343,11 +345,13 @@ 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 }'|tr '\n' ' '; + cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$8 }' |grep -w $i| grep false | awk '{ print $1 }' done) - echo -e "\n$TITLE12" + text_title "$TITLE12" if [[ $COMMAND12 ]]; then - text_warn "Users with Password enabled by MFA disabled: $COMMAND12" + for u in $COMMAND12; do + text_warn "User $u has Password enabled but MFA disabled" + done else text_ok "No users found with Password enabled and MFA disabled" fi @@ -355,7 +359,7 @@ check12(){ check13(){ TITLE13="$BLUE 1.3$NORMAL Ensure credentials unused for 90 days or greater are disabled (Scored)" - echo -e "\n$TITLE13 " + text_title "$TITLE13" 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 COMMAND13=$( @@ -386,7 +390,7 @@ check14(){ TITLE14="$BLUE 1.4$NORMAL 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 }') - echo -e "\n$TITLE14 " + text_title "$TITLE14" C14_NUM_USERS1=0 C14_NUM_USERS2=0 # $(expr $C116_NUM_USERS + 1) @@ -431,7 +435,7 @@ check14(){ check15(){ TITLE15="$BLUE 1.5$NORMAL Ensure IAM password policy requires at least one uppercase letter (Scored)" COMMAND15=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireUppercaseCharacters') # must be true - echo -e "\n$TITLE15 " + text_title "$TITLE15" if [[ $COMMAND15 == "true" ]];then text_ok "Password Policy requires upper case" else @@ -442,7 +446,7 @@ check15(){ check16(){ TITLE16="$BLUE 1.6$NORMAL Ensure IAM password policy require at least one lowercase letter (Scored)" COMMAND16=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireLowercaseCharacters') # must be true - echo -e "\n$TITLE16 " + text_title "$TITLE16" if [[ $COMMAND16 == "true" ]];then text_ok "Password Policy requires lower case" else @@ -453,7 +457,7 @@ check16(){ check17(){ TITLE17="$BLUE 1.7$NORMAL Ensure IAM password policy require at least one symbol (Scored)" COMMAND17=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireSymbols') # must be true - echo -e "\n$TITLE17 " + text_title "$TITLE17" if [[ $COMMAND17 == "true" ]];then text_ok "Password Policy requires symbol" else @@ -464,7 +468,7 @@ check17(){ check18(){ TITLE18="$BLUE 1.8$NORMAL Ensure IAM password policy require at least one number (Scored)" COMMAND18=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireNumbers') # must be true - echo -e "\n$TITLE18 " + text_title "$TITLE18" if [[ $COMMAND18 == "true" ]];then text_ok "Password Policy requires number" else @@ -475,7 +479,7 @@ check18(){ check19(){ TITLE19="$BLUE 1.9$NORMAL Ensure IAM password policy requires minimum length of 14 or greater (Scored)" COMMAND19=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.MinimumPasswordLength') - echo -e "\n$TITLE19 " + text_title "$TITLE19" if [[ $COMMAND19 -gt "13" ]];then text_ok "Password Policy requires more than 13 characters" else @@ -486,7 +490,7 @@ check19(){ check110(){ TITLE110="$BLUE 1.10$NORMAL Ensure IAM password policy prevents password reuse (Scored)" COMMAND110=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.PasswordReusePrevention' --output text) - echo -e "\n$TITLE110 " + text_title "$TITLE110" if [[ $COMMAND110 ]];then if [[ $COMMAND110 -gt "23" ]];then text_ok "Password Policy limits reuse" @@ -501,7 +505,7 @@ check110(){ check111(){ TITLE111="$BLUE 1.11$NORMAL Ensure IAM password policy expires passwords within 90 days or less (Scored)" COMMAND111=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION | grep MaxPasswordAge | awk -F: '{ print $2 }'|sed 's/\ //g'|sed 's/,/ /g') - echo -e "\n$TITLE111 " + text_title "$TITLE111" if [[ $COMMAND111 ]];then if [ $COMMAND111 == "90" ];then text_ok "Password Policy includes expiration" @@ -516,7 +520,7 @@ check112(){ # 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 }') - echo -e "\n$TITLE112 " + text_title "$TITLE112" if [ $ROOTKEY1 == "false" ];then text_ok "No access key 1 found for root" else @@ -532,7 +536,7 @@ check112(){ check113(){ TITLE113="$BLUE 1.13$NORMAL Ensure MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary --profile $PROFILE --region $REGION |grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') - echo -e "\n$TITLE113" + text_title "$TITLE113" if [ $COMMAND113 == "1" ]; then text_ok "Virtual MFA is enabled for root" else @@ -543,7 +547,7 @@ check113(){ check114(){ TITLE114="$BLUE 1.14$NORMAL Ensure hardware MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary --profile $PROFILE --region $REGION |grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') - echo -e "\n$TITLE114" + text_title "$TITLE114" if [ $COMMAND113 == "1" ]; then COMMAND114=$($AWSCLI iam list-virtual-mfa-devices --profile $PROFILE --region $REGION --query 'VirtualMFADevices' --output text|grep :root |wc -l) if [ $COMMAND114 == "1" ]; then @@ -559,7 +563,7 @@ check114(){ check115(){ TITLE115="$BLUE 1.15$NORMAL Ensure security questions are registered in the AWS account (Not Scored)" # No command available - echo -e "\n$TITLE115" + text_title "$TITLE115" text_notice "No command available for check 1.15 " text_notice "Login to the AWS Console as root, click on the Account " text_notice "Name -> My Account -> Configure Security Challenge Questions " @@ -567,7 +571,7 @@ check115(){ check116(){ TITLE116="$BLUE 1.16$NORMAL Ensure IAM policies are attached only to groups or roles (Scored)" - echo -e "\n$TITLE116" + text_title "$TITLE116" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text --profile $PROFILE --region $REGION) C116_NUM_USERS=0 for user in $LIST_USERS;do @@ -585,7 +589,7 @@ check116(){ check117(){ TITLE117="$BLUE 1.17$NORMAL Enable detailed billing (Scored)" # No command available - echo -e "\n$TITLE117 " + text_title "$TITLE117" text_notice "No command available for check 1.17 " text_notice "See section 1.17 on the CIS Benchmark guide for details " infoReferenceShort @@ -593,7 +597,7 @@ check117(){ check118(){ TITLE118="$BLUE 1.18$NORMAL Ensure IAM Master and IAM Manager roles are active (Scored)" - echo -e "\n$TITLE118 " + text_title "$TITLE118" FINDMASTERANDMANAGER=$($AWSCLI iam list-roles --profile $PROFILE --region $REGION --query "Roles[*].{RoleName:RoleName}" --output text | grep -E 'Master|Manager'| tr '\n' ' ') if [[ $FINDMASTERANDMANAGER ]];then text_notice "Found next roles as possible IAM Master and IAM Manager candidates: " @@ -619,7 +623,7 @@ check118(){ check119(){ TITLE119="$BLUE 1.19$NORMAL Maintain current contact details (Scored)" # No command available - echo -e "\n$TITLE119 " + text_title "$TITLE119" text_notice "No command available for check 1.19 " text_notice "See section 1.19 on the CIS Benchmark guide for details " infoReferenceShort @@ -628,7 +632,7 @@ check119(){ check120(){ TITLE120="$BLUE 1.20$NORMAL Ensure security contact information is registered (Scored)" # No command available - echo -e "\n$TITLE120 " + text_title "$TITLE120" text_notice "No command available for check 1.20 " text_notice "See section 1.20 on the CIS Benchmark guide for details " infoReferenceShort @@ -636,7 +640,7 @@ check120(){ check121(){ TITLE121="$BLUE 1.21$NORMAL Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)" - echo -e "\n$TITLE121 " + text_title "$TITLE121" text_notice "No command available for check 1.21 " text_notice "See section 1.21 on the CIS Benchmark guide for details " infoReferenceShort @@ -644,7 +648,7 @@ check121(){ check122(){ TITLE122="$BLUE 1.22$NORMAL Ensure a support role has been created to manage incidents with AWS Support (Scored)" - echo -e "\n$TITLE122 " + text_title "$TITLE122" SUPPORTPOLICYARN=$($AWSCLI iam list-policies --query "Policies[?PolicyName == 'AWSSupportAccess'].Arn" --profile $PROFILE --region $REGION --output text) if [[ $SUPPORTPOLICYARN ]];then for policyarn in $SUPPORTPOLICYARN;do @@ -663,14 +667,14 @@ check122(){ check123(){ TITLE123="$BLUE 1.23$NORMAL Do not setup access keys during initial user setup for all IAM users that have a console password (Not Scored)" - echo -e "\n$TITLE123 " + text_title "$TITLE123" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text --profile $PROFILE --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) if [[ $LIST_USERS_KEY1_ACTIVE ]]; then - text_notice "List of users with Access Key 1 never used:" - text_notice "$LIST_USERS_KEY1_ACTIVE have never used Access Key 1" + # text_notice "List of users with Access Key 1 never used:" + text_notice "Users who never used Access Key 1: $LIST_USERS_KEY1_ACTIVE" else text_ok "No users found with Access Key 1 never used" fi @@ -678,8 +682,8 @@ check123(){ 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 - text_notice "List of users with Access Key 2 never used:" - text_notice "$LIST_USERS_KEY2_ACTIVE have never used Access Key 2" + # text_notice "List of users with Access Key 2 never used:" + text_notice "Users who never used Access Key 2: $LIST_USERS_KEY2_ACTIVE" else text_ok "No users found with Access Key 2 never used" fi @@ -687,7 +691,7 @@ check123(){ check124(){ TITLE124="$BLUE 1.24$NORMAL Ensure IAM policies that allow full \"*:*\" administrative privileges are not created (Scored)" - echo -e "\n$TITLE124" + text_title "$TITLE124" LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text --profile $PROFILE --region $REGION|grep 'arn:aws:iam::[0-9]\{12\}:'|awk '{ print $2 }') if [[ $LIST_CUSTOM_POLICIES ]]; then text_notice "Looking for custom policies: (skipping default policies, it may take few seconds...)" @@ -713,7 +717,7 @@ check124(){ check21(){ TITLE21="$BLUE 2.1$NORMAL Ensure CloudTrail is enabled in all regions (Scored)" - echo -e "\n$TITLE21" + text_title "$TITLE21" LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].Name' --output text) if [[ $LIST_OF_TRAILS ]];then for trail in $LIST_OF_TRAILS;do @@ -731,7 +735,7 @@ check21(){ check22(){ TITLE22="$BLUE 2.2$NORMAL Ensure CloudTrail log file validation is enabled (Scored)" - echo -e "\n$TITLE22" + text_title "$TITLE22" LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].Name' --output text) if [[ $LIST_OF_TRAILS ]];then for trail in $LIST_OF_TRAILS;do @@ -749,7 +753,7 @@ check22(){ check23(){ TITLE23="$BLUE 2.3$NORMAL Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)" - echo -e "\n$TITLE23" + text_title "$TITLE23" CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION) if [[ $CLOUDTRAILBUCKET ]];then for bucket in $CLOUDTRAILBUCKET;do @@ -767,7 +771,7 @@ check23(){ check24(){ TITLE24="$BLUE 2.4$NORMAL Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)" - echo -e "\n$TITLE24" + text_title "$TITLE24" TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion}' --output text | tr "\t" ',') if [[ $TRAILS_AND_REGIONS ]];then for reg_trail in $TRAILS_AND_REGIONS;do @@ -793,7 +797,7 @@ check24(){ check25(){ TITLE25="$BLUE 2.5$NORMAL Ensure AWS Config is enabled in all regions (Scored)" - echo -e "\n$TITLE25" + text_title "$TITLE25" for regx in $REGIONS; do CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status --profile $PROFILE --region $regx | grep "recorder: ON") if [[ $CHECK_AWSCONFIG_STATUS ]];then @@ -806,7 +810,7 @@ check25(){ check26(){ TITLE26="$BLUE 2.6$NORMAL Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" - echo -e "\n$TITLE26" + text_title "$TITLE26" CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION) if [[ $CLOUDTRAILBUCKET ]];then for bucket in $CLOUDTRAILBUCKET;do @@ -824,7 +828,7 @@ check26(){ check27(){ TITLE27="$BLUE 2.7$NORMAL Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)" - echo -e "\n$TITLE27" + text_title "$TITLE27" CLOUDTRAILNAME=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].Name' --output text --profile $PROFILE --region $REGION) if [[ $CLOUDTRAILNAME ]];then for trail in $CLOUDTRAILNAME;do @@ -842,7 +846,7 @@ check27(){ check28(){ TITLE28="$BLUE 2.8$NORMAL Ensure rotation for customer created CMKs is enabled (Scored)" - echo -e "\n$TITLE28" + text_title "$TITLE28" for regx in $REGIONS; do CHECK_KMS_KEYLIST=$($AWSCLI kms list-keys --profile $PROFILE --region $regx --output text --query 'Keys[*].KeyId') if [[ $CHECK_KMS_KEYLIST ]];then @@ -872,7 +876,7 @@ check28(){ check31(){ TITLE31="$BLUE 3.1$NORMAL Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" - echo -e "\n$TITLE31 " + text_title "$TITLE31" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep AccessDenied) @@ -888,7 +892,7 @@ check31(){ check32(){ TITLE32="$BLUE 3.2$NORMAL Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" - echo -e "\n$TITLE32 " + text_title "$TITLE32" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'userIdentity.sessionContext.attributes.mfaAuthenticated.*true') @@ -904,7 +908,7 @@ check32(){ check33(){ TITLE33="$BLUE 3.3$NORMAL Ensure a log metric filter and alarm exist for usage of root account (Scored)" - echo -e "\n$TITLE33 " + text_title "$TITLE33" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent') @@ -920,7 +924,7 @@ check33(){ check34(){ TITLE34="$BLUE 3.4$NORMAL Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" - echo -e "\n$TITLE34 " + text_title "$TITLE34" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'DeleteGroupPolicy.*DeleteRolePolicy.*DeleteUserPolicy.*PutGroupPolicy.*PutRolePolicy.*PutUserPolicy.*CreatePolicy.*DeletePolicy.*CreatePolicyVersion.*DeletePolicyVersion.*AttachRolePolicy.*DetachRolePolicy.*AttachUserPolicy.*DetachUserPolicy.*AttachGroupPolicy.*DetachGroupPolicy') @@ -936,7 +940,7 @@ check34(){ check35(){ TITLE35="$BLUE 3.5$NORMAL Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" - echo -e "\n$TITLE35 " + text_title "$TITLE35" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging') @@ -952,7 +956,7 @@ check35(){ check36(){ TITLE36="$BLUE 3.6$NORMAL Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" - echo -e "\n$TITLE36 " + text_title "$TITLE36" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed') @@ -968,7 +972,7 @@ check36(){ check37(){ TITLE37="$BLUE 3.7$NORMAL Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" - echo -e "\n$TITLE37 " + text_title "$TITLE37" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion') @@ -984,7 +988,7 @@ check37(){ check38(){ TITLE38="$BLUE 3.8$NORMAL Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" - echo -e "\n$TITLE38 " + text_title "$TITLE38" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication') @@ -1000,7 +1004,7 @@ check38(){ check39(){ TITLE39="$BLUE 3.9$NORMAL Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" - echo -e "\n$TITLE39 " + text_title "$TITLE39" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder') @@ -1016,7 +1020,7 @@ check39(){ check310(){ TITLE310="$BLUE 3.10$NORMAL Ensure a log metric filter and alarm exist for security group changes (Scored)" - echo -e "\n$TITLE310 " + text_title "$TITLE310" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup') @@ -1032,7 +1036,7 @@ check310(){ check311(){ TITLE311="$BLUE 3.11$NORMAL Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" - echo -e "\n$TITLE311 " + text_title "$TITLE311" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation') @@ -1048,7 +1052,7 @@ check311(){ check312(){ TITLE312="$BLUE 3.12$NORMAL Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" - echo -e "\n$TITLE312 " + text_title "$TITLE312" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway') @@ -1064,7 +1068,7 @@ check312(){ check313(){ TITLE313="$BLUE 3.13$NORMAL Ensure a log metric filter and alarm exist for route table changes (Scored)" - echo -e "\n$TITLE313 " + text_title "$TITLE313" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable') @@ -1080,7 +1084,7 @@ check313(){ check314(){ TITLE314="$BLUE 3.14$NORMAL Ensure a log metric filter and alarm exist for VPC changes (Scored)" - echo -e "\n$TITLE314 " + text_title "$TITLE314" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink') @@ -1096,7 +1100,7 @@ check314(){ check315(){ TITLE315="$BLUE 3.15$NORMAL Ensure appropriate subscribers to each SNS topic (Not Scored)" - echo -e "\n$TITLE315 " + text_title "$TITLE315" for regx in $REGIONS; do TOPICS_LIST=$($AWSCLI sns list-topics --profile $PROFILE --region $regx --output text --query 'Topics[*].TopicArn') if [[ $TOPICS_LIST ]];then @@ -1119,7 +1123,7 @@ check315(){ check41(){ TITLE41="$BLUE 4.1$NORMAL Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored)" - echo -e "\n$TITLE41 " + text_title "$TITLE41" 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`].{GroupName: GroupName}' --profile $PROFILE --region $regx --output text) if [[ $SG_LIST ]];then @@ -1134,7 +1138,7 @@ check41(){ check42(){ TITLE42="$BLUE 4.2$NORMAL Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored)" - echo -e "\n$TITLE42 " + text_title "$TITLE42" 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 $PROFILE --region $regx --output text) if [[ $SG_LIST ]];then @@ -1149,7 +1153,7 @@ check42(){ check43(){ TITLE43="$BLUE 4.3$NORMAL Ensure VPC Flow Logging is Enabled in all VPCs (Scored)" - echo -e "\n$TITLE43 " + text_title "$TITLE43" for regx in $REGIONS; do CHECK_FL=$($AWSCLI ec2 describe-flow-logs --profile $PROFILE --region $regx --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].LogGroupName' --output text) if [[ $CHECK_FL ]];then @@ -1164,7 +1168,7 @@ check43(){ check44(){ TITLE44="$BLUE 4.4$NORMAL Ensure the default security group of every VPC restricts all traffic (Scored)" - echo -e "\n$TITLE44 " + text_title "$TITLE44" for regx in $REGIONS; do CHECK_SGDEFAULT=$($AWSCLI ec2 describe-security-groups --profile $PROFILE --region $regx --filters Name=group-name,Values='default' --query 'SecurityGroups[*].{IpPermissions:IpPermissions,IpPermissionsEgress:IpPermissionsEgress,GroupId:GroupId}' --output text |grep 0.0.0.0) if [[ $CHECK_SGDEFAULT ]];then @@ -1178,7 +1182,7 @@ check44(){ check45(){ #set -xe TITLE45="$BLUE 4.5$NORMAL Ensure routing tables for VPC peering are \"least access\" (Not Scored)" - echo -e "\n$TITLE45 " + text_title "$TITLE45" text_notice "Looking for VPC peering in all regions... " for regx in $REGIONS; do LIST_OF_VPCS_PEERING_CONNECTIONS=$($AWSCLI ec2 describe-vpc-peering-connections --output text --profile $PROFILE --region $regx --query 'VpcPeeringConnections[*].VpcPeeringConnectionId') @@ -1199,76 +1203,76 @@ check45(){ callCheck(){ if [[ $CHECKNUMBER ]];then case "$CHECKNUMBER" in - check11) check11;; - check12) check12;; - check13) check13;; - check14) check14;; - check15) check15;; - check16) check16;; - check17) check17;; - check18) check18;; - check19) check19;; - check110) check110;; - check111) check111;; - check112) check112;; - check113) check113;; - check114) check114;; - check115) check115;; - check116) check116;; - check117) check117;; - check118) check118;; - check119) check119;; - check120) check120;; - check121) check121;; - check122) check122;; - check123) check123;; - check124) check124;; - check21) check21;; - check22) check22;; - check23) check23;; - check24) check24;; - check25) check25;; - check26) check26;; - check27) check27;; - check28) check28;; - check31) check31;; - check32) check32;; - check33) check33;; - check34) check34;; - check35) check35;; - check36) check36;; - check37) check37;; - check38) check38;; - check39) check39;; - check310) check310;; - check311) check311;; - check312) check312;; - check313) check313;; - check314) check314;; - check315) check315;; - check41) check41;; - check42) check42;; - check43) check43;; - check44) check44;; - check45) check45;; - check1) + check11 ) check11;; + check12 ) check12;; + check13 ) check13;; + check14 ) check14;; + check15 ) check15;; + check16 ) check16;; + check17 ) check17;; + check18 ) check18;; + check19 ) check19;; + check110 ) check110;; + check111 ) check111;; + check112 ) check112;; + check113 ) check113;; + check114 ) check114;; + check115 ) check115;; + check116 ) check116;; + check117 ) check117;; + check118 ) check118;; + check119 ) check119;; + check120 ) check120;; + check121 ) check121;; + check122 ) check122;; + check123 ) check123;; + check124 ) check124;; + check21 ) check21;; + check22 ) check22;; + check23 ) check23;; + check24 ) check24;; + check25 ) check25;; + check26 ) check26;; + check27 ) check27;; + check28 ) check28;; + check31 ) check31;; + check32 ) check32;; + check33 ) check33;; + check34 ) check34;; + check35 ) check35;; + check36 ) check36;; + check37 ) check37;; + check38 ) check38;; + check39 ) check39;; + check310 ) check310;; + check311 ) check311;; + check312 ) check312;; + check313 ) check313;; + check314 ) check314;; + check315 ) check315;; + check41 ) check41;; + check42 ) check42;; + check43 ) check43;; + check44 ) check44;; + check45 ) check45;; + check1 ) check11;check12;check13;check14;check15;check16;check17;check18; check19;check110;check111;check112;check113;check114;check115; check116;check117;check118;check119;check120;check121;check122; check123;check124; ;; - check2) + check2 ) check21;check22;check23;check24;check25;check26;check27;check28 ;; - check3) + check3 ) check31;check32;check33;check34;check35;check36;check37;check38; check39;check310;check311;check312;check313;check314;check315 ;; - check4) + check4 ) check41;check42;check43;check44;check45 ;; * ) - echo -e "\n$RED ERROR! Use a valid check name (i.e. check41) $NORMAL\n"; + text_warn "ERROR! Use a valid check name (i.e. check41)\n"; esac cleanTemp exit @@ -1289,7 +1293,7 @@ saveReport callCheck TITLE1="$BLUE 1 Identity and Access Management *********************************$NORMAL" -echo -e "\n\n$TITLE1 " +echo -e "\n\n$TITLE1" check11 check12 check13 @@ -1316,7 +1320,7 @@ check123 check124 TITLE2="$BLUE 2 Logging ********************************************************$NORMAL" -echo -e "\n\n$TITLE2 " +echo -e "\n\n$TITLE2" check21 check22 check23 @@ -1327,7 +1331,7 @@ check27 check28 TITLE3="$BLUE 3 Monitoring *****************************************************" -echo -e "\n\n$TITLE3 " +echo -e "\n\n$TITLE3" # 3 Monitoring check commands / Mostly covered by SecurityMonkey check31 check32 @@ -1346,7 +1350,7 @@ check314 check315 TITLE4="$BLUE 4 Networking **************************************************$NORMAL" -echo -e "\n\n$TITLE4 " +echo -e "\n\n$TITLE4" check41 check42 check43 From 6334e197305b069c02a6e4b63b61ecd62cd2698c Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 14:53:45 -0500 Subject: [PATCH 10/22] pull ID out of title, and pass it to text_title --- prowler | 270 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 162 insertions(+), 108 deletions(-) diff --git a/prowler b/prowler index 72555387..616c7ac1 100755 --- a/prowler +++ b/prowler @@ -240,21 +240,23 @@ fi # AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} # AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY_ID} +TITLE_ID="" ## Output formatting functions text_ok(){ - echo " $OK OK! $NORMAL $@" + echo " $TITLE_ID $OK OK! $NORMAL $@" } text_notice(){ - echo " $NOTICE NOTICE! $@ $NORMAL" + echo " $TITLE_ID $NOTICE INFO! $@ $NORMAL" } text_warn(){ - echo " $BAD WARNING! $@ $NORMAL" + echo " $TITLE_ID $BAD WARNING! $@ $NORMAL" } text_title(){ - echo -e "\n$@" + TITLE_ID=$1 + echo -e "\n$BLUE $TITLE_ID $NORMAL ${@:2}" } prowlerBanner() { @@ -333,21 +335,23 @@ infoReferenceShort(){ } check11(){ - TITLE11="$BLUE 1.1$NORMAL Avoid the use of the root account (Scored)." + ID11="1.1" + TITLE11="Avoid the use of the root account (Scored)." COMMAND11=$(cat $TEMP_REPORT_FILE| grep '' | cut -d, -f5,11,16 | sed 's/,/,\ /g') - text_title "$TITLE11" + text_title "$ID11" "$TITLE11" text_notice "Root account last accessed (password, key_1, key_2): $COMMAND11" } check12(){ - TITLE12="$BLUE 1.2$NORMAL Ensure multi-factor authentication (MFA) is enabled for all IAM users that have a console password (Scored)" + ID12="1.2" + TITLE12="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=$( 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 }' done) - text_title "$TITLE12" + text_title "$ID12" "$TITLE12" if [[ $COMMAND12 ]]; then for u in $COMMAND12; do text_warn "User $u has Password enabled but MFA disabled" @@ -358,8 +362,9 @@ check12(){ } check13(){ - TITLE13="$BLUE 1.3$NORMAL Ensure credentials unused for 90 days or greater are disabled (Scored)" - text_title "$TITLE13" + ID13="1.3" + TITLE13="Ensure credentials unused for 90 days or greater are disabled (Scored)" + text_title "$ID13" "$TITLE13" 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 COMMAND13=$( @@ -387,10 +392,11 @@ check13(){ } check14(){ - TITLE14="$BLUE 1.4$NORMAL Ensure access keys are rotated every 90 days or less (Scored)" # also checked by Security Monkey + ID14="1.4" + TITLE14="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 }') - text_title "$TITLE14" + text_title "$ID14" "$TITLE14" C14_NUM_USERS1=0 C14_NUM_USERS2=0 # $(expr $C116_NUM_USERS + 1) @@ -433,9 +439,10 @@ check14(){ } check15(){ - TITLE15="$BLUE 1.5$NORMAL Ensure IAM password policy requires at least one uppercase letter (Scored)" + ID15="1.5" + TITLE15="Ensure IAM password policy requires at least one uppercase letter (Scored)" COMMAND15=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireUppercaseCharacters') # must be true - text_title "$TITLE15" + text_title "$ID15" "$TITLE15" if [[ $COMMAND15 == "true" ]];then text_ok "Password Policy requires upper case" else @@ -444,9 +451,10 @@ check15(){ } check16(){ - TITLE16="$BLUE 1.6$NORMAL Ensure IAM password policy require at least one lowercase letter (Scored)" + ID16="1.6" + TITLE16="Ensure IAM password policy require at least one lowercase letter (Scored)" COMMAND16=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireLowercaseCharacters') # must be true - text_title "$TITLE16" + text_title "$ID16" "$TITLE16" if [[ $COMMAND16 == "true" ]];then text_ok "Password Policy requires lower case" else @@ -455,9 +463,10 @@ check16(){ } check17(){ - TITLE17="$BLUE 1.7$NORMAL Ensure IAM password policy require at least one symbol (Scored)" + ID17="1.7" + TITLE17="Ensure IAM password policy require at least one symbol (Scored)" COMMAND17=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireSymbols') # must be true - text_title "$TITLE17" + text_title "$ID17" "$TITLE17" if [[ $COMMAND17 == "true" ]];then text_ok "Password Policy requires symbol" else @@ -466,9 +475,10 @@ check17(){ } check18(){ - TITLE18="$BLUE 1.8$NORMAL Ensure IAM password policy require at least one number (Scored)" + ID18="1.8" + TITLE18="Ensure IAM password policy require at least one number (Scored)" COMMAND18=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireNumbers') # must be true - text_title "$TITLE18" + text_title "$ID18" "$TITLE18" if [[ $COMMAND18 == "true" ]];then text_ok "Password Policy requires number" else @@ -477,9 +487,10 @@ check18(){ } check19(){ - TITLE19="$BLUE 1.9$NORMAL Ensure IAM password policy requires minimum length of 14 or greater (Scored)" + ID19="1.9" + TITLE19="Ensure IAM password policy requires minimum length of 14 or greater (Scored)" COMMAND19=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.MinimumPasswordLength') - text_title "$TITLE19" + text_title "$ID19" "$TITLE19" if [[ $COMMAND19 -gt "13" ]];then text_ok "Password Policy requires more than 13 characters" else @@ -488,9 +499,10 @@ check19(){ } check110(){ - TITLE110="$BLUE 1.10$NORMAL Ensure IAM password policy prevents password reuse (Scored)" + ID110="1.10" + TITLE110="Ensure IAM password policy prevents password reuse (Scored)" COMMAND110=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.PasswordReusePrevention' --output text) - text_title "$TITLE110" + text_title "$ID110" "$TITLE110" if [[ $COMMAND110 ]];then if [[ $COMMAND110 -gt "23" ]];then text_ok "Password Policy limits reuse" @@ -503,9 +515,10 @@ check110(){ } check111(){ - TITLE111="$BLUE 1.11$NORMAL Ensure IAM password policy expires passwords within 90 days or less (Scored)" + ID111="1.11" + TITLE111="Ensure IAM password policy expires passwords within 90 days or less (Scored)" COMMAND111=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION | grep MaxPasswordAge | awk -F: '{ print $2 }'|sed 's/\ //g'|sed 's/,/ /g') - text_title "$TITLE111" + text_title "$ID111" "$TITLE111" if [[ $COMMAND111 ]];then if [ $COMMAND111 == "90" ];then text_ok "Password Policy includes expiration" @@ -516,11 +529,12 @@ check111(){ } check112(){ - TITLE112="$BLUE 1.12$NORMAL Ensure no root account access key exists (Scored)" + ID112="1.12" + TITLE112="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 }') - text_title "$TITLE112" + text_title "$ID112" "$TITLE112" if [ $ROOTKEY1 == "false" ];then text_ok "No access key 1 found for root" else @@ -534,9 +548,10 @@ check112(){ } check113(){ - TITLE113="$BLUE 1.13$NORMAL Ensure MFA is enabled for the root account (Scored)" + ID113="1.13" + TITLE113="Ensure MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary --profile $PROFILE --region $REGION |grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') - text_title "$TITLE113" + text_title "$ID113" "$TITLE113" if [ $COMMAND113 == "1" ]; then text_ok "Virtual MFA is enabled for root" else @@ -545,9 +560,10 @@ check113(){ } check114(){ - TITLE114="$BLUE 1.14$NORMAL Ensure hardware MFA is enabled for the root account (Scored)" + ID114="1.14" + TITLE114="Ensure hardware MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary --profile $PROFILE --region $REGION |grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') - text_title "$TITLE114" + text_title "$ID114" "$TITLE114" if [ $COMMAND113 == "1" ]; then COMMAND114=$($AWSCLI iam list-virtual-mfa-devices --profile $PROFILE --region $REGION --query 'VirtualMFADevices' --output text|grep :root |wc -l) if [ $COMMAND114 == "1" ]; then @@ -561,17 +577,19 @@ check114(){ } check115(){ - TITLE115="$BLUE 1.15$NORMAL Ensure security questions are registered in the AWS account (Not Scored)" + ID115="1.15" + TITLE115="Ensure security questions are registered in the AWS account (Not Scored)" # No command available - text_title "$TITLE115" + text_title "$ID115" "$TITLE115" text_notice "No command available for check 1.15 " text_notice "Login to the AWS Console as root, click on the Account " text_notice "Name -> My Account -> Configure Security Challenge Questions " } check116(){ - TITLE116="$BLUE 1.16$NORMAL Ensure IAM policies are attached only to groups or roles (Scored)" - text_title "$TITLE116" + ID116="1.16" + TITLE116="Ensure IAM policies are attached only to groups or roles (Scored)" + text_title "$ID116" "$TITLE116" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text --profile $PROFILE --region $REGION) C116_NUM_USERS=0 for user in $LIST_USERS;do @@ -587,17 +605,19 @@ check116(){ } check117(){ - TITLE117="$BLUE 1.17$NORMAL Enable detailed billing (Scored)" + ID117="1.17" + TITLE117="Enable detailed billing (Scored)" # No command available - text_title "$TITLE117" + text_title "$ID117" "$TITLE117" text_notice "No command available for check 1.17 " text_notice "See section 1.17 on the CIS Benchmark guide for details " infoReferenceShort } check118(){ - TITLE118="$BLUE 1.18$NORMAL Ensure IAM Master and IAM Manager roles are active (Scored)" - text_title "$TITLE118" + ID118="1.18" + TITLE118="Ensure IAM Master and IAM Manager roles are active (Scored)" + text_title "$ID118" "$TITLE118" FINDMASTERANDMANAGER=$($AWSCLI iam list-roles --profile $PROFILE --region $REGION --query "Roles[*].{RoleName:RoleName}" --output text | grep -E 'Master|Manager'| tr '\n' ' ') if [[ $FINDMASTERANDMANAGER ]];then text_notice "Found next roles as possible IAM Master and IAM Manager candidates: " @@ -621,34 +641,38 @@ check118(){ } check119(){ - TITLE119="$BLUE 1.19$NORMAL Maintain current contact details (Scored)" + ID119="1.19" + TITLE119="Maintain current contact details (Scored)" # No command available - text_title "$TITLE119" + text_title "$ID119" "$TITLE119" text_notice "No command available for check 1.19 " text_notice "See section 1.19 on the CIS Benchmark guide for details " infoReferenceShort } check120(){ - TITLE120="$BLUE 1.20$NORMAL Ensure security contact information is registered (Scored)" + ID120="1.20" + TITLE120="Ensure security contact information is registered (Scored)" # No command available - text_title "$TITLE120" + text_title "$ID120" "$TITLE120" text_notice "No command available for check 1.20 " text_notice "See section 1.20 on the CIS Benchmark guide for details " infoReferenceShort } check121(){ - TITLE121="$BLUE 1.21$NORMAL Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)" - text_title "$TITLE121" + ID121="1.21" + TITLE121="Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)" + text_title "$ID121" "$TITLE121" text_notice "No command available for check 1.21 " text_notice "See section 1.21 on the CIS Benchmark guide for details " infoReferenceShort } check122(){ - TITLE122="$BLUE 1.22$NORMAL Ensure a support role has been created to manage incidents with AWS Support (Scored)" - text_title "$TITLE122" + ID122="1.22" + TITLE122="Ensure a support role has been created to manage incidents with AWS Support (Scored)" + text_title "$ID122" "$TITLE122" SUPPORTPOLICYARN=$($AWSCLI iam list-policies --query "Policies[?PolicyName == 'AWSSupportAccess'].Arn" --profile $PROFILE --region $REGION --output text) if [[ $SUPPORTPOLICYARN ]];then for policyarn in $SUPPORTPOLICYARN;do @@ -666,8 +690,9 @@ check122(){ } check123(){ - TITLE123="$BLUE 1.23$NORMAL Do not setup access keys during initial user setup for all IAM users that have a console password (Not Scored)" - text_title "$TITLE123" + ID123="1.23" + TITLE123="Do not setup access keys during initial user setup for all IAM users that have a console password (Not Scored)" + text_title "$ID123" "$TITLE123" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text --profile $PROFILE --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) @@ -690,8 +715,9 @@ check123(){ } check124(){ - TITLE124="$BLUE 1.24$NORMAL Ensure IAM policies that allow full \"*:*\" administrative privileges are not created (Scored)" - text_title "$TITLE124" + ID124="1.24" + TITLE124="Ensure IAM policies that allow full \"*:*\" administrative privileges are not created (Scored)" + text_title "$ID124" "$TITLE124" LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text --profile $PROFILE --region $REGION|grep 'arn:aws:iam::[0-9]\{12\}:'|awk '{ print $2 }') if [[ $LIST_CUSTOM_POLICIES ]]; then text_notice "Looking for custom policies: (skipping default policies, it may take few seconds...)" @@ -716,8 +742,9 @@ check124(){ } check21(){ - TITLE21="$BLUE 2.1$NORMAL Ensure CloudTrail is enabled in all regions (Scored)" - text_title "$TITLE21" + ID21="2.1" + TITLE21="Ensure CloudTrail is enabled in all regions (Scored)" + text_title "$ID21" "$TITLE21" LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].Name' --output text) if [[ $LIST_OF_TRAILS ]];then for trail in $LIST_OF_TRAILS;do @@ -734,8 +761,9 @@ check21(){ } check22(){ - TITLE22="$BLUE 2.2$NORMAL Ensure CloudTrail log file validation is enabled (Scored)" - text_title "$TITLE22" + ID22="2.2" + TITLE22="Ensure CloudTrail log file validation is enabled (Scored)" + text_title "$ID22" "$TITLE22" LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].Name' --output text) if [[ $LIST_OF_TRAILS ]];then for trail in $LIST_OF_TRAILS;do @@ -752,8 +780,9 @@ check22(){ } check23(){ - TITLE23="$BLUE 2.3$NORMAL Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)" - text_title "$TITLE23" + ID23="2.3" + TITLE23="Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)" + text_title "$ID23" "$TITLE23" CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION) if [[ $CLOUDTRAILBUCKET ]];then for bucket in $CLOUDTRAILBUCKET;do @@ -770,8 +799,9 @@ check23(){ } check24(){ - TITLE24="$BLUE 2.4$NORMAL Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)" - text_title "$TITLE24" + ID24="2.4" + TITLE24="Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)" + text_title "$ID24" "$TITLE24" TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion}' --output text | tr "\t" ',') if [[ $TRAILS_AND_REGIONS ]];then for reg_trail in $TRAILS_AND_REGIONS;do @@ -796,8 +826,9 @@ check24(){ } check25(){ - TITLE25="$BLUE 2.5$NORMAL Ensure AWS Config is enabled in all regions (Scored)" - text_title "$TITLE25" + ID25="2.5" + TITLE25="Ensure AWS Config is enabled in all regions (Scored)" + text_title "$ID25" "$TITLE25" for regx in $REGIONS; do CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status --profile $PROFILE --region $regx | grep "recorder: ON") if [[ $CHECK_AWSCONFIG_STATUS ]];then @@ -809,8 +840,9 @@ check25(){ } check26(){ - TITLE26="$BLUE 2.6$NORMAL Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" - text_title "$TITLE26" + ID26="2.6" + TITLE26="Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" + text_title "$ID26" "$TITLE26" CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION) if [[ $CLOUDTRAILBUCKET ]];then for bucket in $CLOUDTRAILBUCKET;do @@ -827,8 +859,9 @@ check26(){ } check27(){ - TITLE27="$BLUE 2.7$NORMAL Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)" - text_title "$TITLE27" + ID27="2.7" + TITLE27="Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)" + text_title "$ID27" "$TITLE27" CLOUDTRAILNAME=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].Name' --output text --profile $PROFILE --region $REGION) if [[ $CLOUDTRAILNAME ]];then for trail in $CLOUDTRAILNAME;do @@ -845,8 +878,9 @@ check27(){ } check28(){ - TITLE28="$BLUE 2.8$NORMAL Ensure rotation for customer created CMKs is enabled (Scored)" - text_title "$TITLE28" + ID28="2.8" + TITLE28="Ensure rotation for customer created CMKs is enabled (Scored)" + text_title "$ID28" "$TITLE28" for regx in $REGIONS; do CHECK_KMS_KEYLIST=$($AWSCLI kms list-keys --profile $PROFILE --region $regx --output text --query 'Keys[*].KeyId') if [[ $CHECK_KMS_KEYLIST ]];then @@ -875,8 +909,9 @@ check28(){ } check31(){ - TITLE31="$BLUE 3.1$NORMAL Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" - text_title "$TITLE31" + ID31="3.1" + TITLE31="Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" + text_title "$ID31" "$TITLE31" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep AccessDenied) @@ -891,8 +926,9 @@ check31(){ } check32(){ - TITLE32="$BLUE 3.2$NORMAL Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" - text_title "$TITLE32" + ID32="3.2" + TITLE32="Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" + text_title "$ID32" "$TITLE32" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'userIdentity.sessionContext.attributes.mfaAuthenticated.*true') @@ -907,8 +943,9 @@ check32(){ } check33(){ - TITLE33="$BLUE 3.3$NORMAL Ensure a log metric filter and alarm exist for usage of root account (Scored)" - text_title "$TITLE33" + ID33="3.3" + TITLE33="Ensure a log metric filter and alarm exist for usage of root account (Scored)" + text_title "$ID33" "$TITLE33" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent') @@ -923,8 +960,9 @@ check33(){ } check34(){ - TITLE34="$BLUE 3.4$NORMAL Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" - text_title "$TITLE34" + ID34="3.4" + TITLE34="Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" + text_title "$ID34" "$TITLE34" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'DeleteGroupPolicy.*DeleteRolePolicy.*DeleteUserPolicy.*PutGroupPolicy.*PutRolePolicy.*PutUserPolicy.*CreatePolicy.*DeletePolicy.*CreatePolicyVersion.*DeletePolicyVersion.*AttachRolePolicy.*DetachRolePolicy.*AttachUserPolicy.*DetachUserPolicy.*AttachGroupPolicy.*DetachGroupPolicy') @@ -939,8 +977,9 @@ check34(){ } check35(){ - TITLE35="$BLUE 3.5$NORMAL Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" - text_title "$TITLE35" + ID35="3.5" + TITLE35="Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" + text_title "$ID35" "$TITLE35" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging') @@ -955,8 +994,9 @@ check35(){ } check36(){ - TITLE36="$BLUE 3.6$NORMAL Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" - text_title "$TITLE36" + ID36="3.6" + TITLE36="Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" + text_title "$ID36" "$TITLE36" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed') @@ -971,8 +1011,9 @@ check36(){ } check37(){ - TITLE37="$BLUE 3.7$NORMAL Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" - text_title "$TITLE37" + ID37="3.7" + TITLE37="Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" + text_title "$ID37" "$TITLE37" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion') @@ -987,8 +1028,9 @@ check37(){ } check38(){ - TITLE38="$BLUE 3.8$NORMAL Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" - text_title "$TITLE38" + ID38="3.8" + TITLE38="Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" + text_title "$ID38" "$TITLE38" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication') @@ -1003,8 +1045,9 @@ check38(){ } check39(){ - TITLE39="$BLUE 3.9$NORMAL Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" - text_title "$TITLE39" + ID39="3.9" + TITLE39="Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" + text_title "$ID39" "$TITLE39" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder') @@ -1019,8 +1062,9 @@ check39(){ } check310(){ - TITLE310="$BLUE 3.10$NORMAL Ensure a log metric filter and alarm exist for security group changes (Scored)" - text_title "$TITLE310" + ID310="3.10" + TITLE310="Ensure a log metric filter and alarm exist for security group changes (Scored)" + text_title "$ID310" "$TITLE310" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup') @@ -1035,8 +1079,9 @@ check310(){ } check311(){ - TITLE311="$BLUE 3.11$NORMAL Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" - text_title "$TITLE311" + ID311="3.11" + TITLE311="Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" + text_title "$ID311" "$TITLE311" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation') @@ -1051,8 +1096,9 @@ check311(){ } check312(){ - TITLE312="$BLUE 3.12$NORMAL Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" - text_title "$TITLE312" + ID312="3.12" + TITLE312="Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" + text_title "$ID312" "$TITLE312" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway') @@ -1067,8 +1113,9 @@ check312(){ } check313(){ - TITLE313="$BLUE 3.13$NORMAL Ensure a log metric filter and alarm exist for route table changes (Scored)" - text_title "$TITLE313" + ID313="3.13" + TITLE313="Ensure a log metric filter and alarm exist for route table changes (Scored)" + text_title "$ID313" "$TITLE313" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable') @@ -1083,8 +1130,9 @@ check313(){ } check314(){ - TITLE314="$BLUE 3.14$NORMAL Ensure a log metric filter and alarm exist for VPC changes (Scored)" - text_title "$TITLE314" + ID314="3.14" + TITLE314="Ensure a log metric filter and alarm exist for VPC changes (Scored)" + text_title "$ID314" "$TITLE314" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink') @@ -1099,8 +1147,9 @@ check314(){ } check315(){ - TITLE315="$BLUE 3.15$NORMAL Ensure appropriate subscribers to each SNS topic (Not Scored)" - text_title "$TITLE315" + ID315="3.15" + TITLE315="Ensure appropriate subscribers to each SNS topic (Not Scored)" + text_title "$ID315" "$TITLE315" for regx in $REGIONS; do TOPICS_LIST=$($AWSCLI sns list-topics --profile $PROFILE --region $regx --output text --query 'Topics[*].TopicArn') if [[ $TOPICS_LIST ]];then @@ -1122,8 +1171,9 @@ check315(){ } check41(){ - TITLE41="$BLUE 4.1$NORMAL Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored)" - text_title "$TITLE41" + ID41="4.1" + TITLE41="Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored)" + text_title "$ID41" "$TITLE41" 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`].{GroupName: GroupName}' --profile $PROFILE --region $regx --output text) if [[ $SG_LIST ]];then @@ -1137,8 +1187,9 @@ check41(){ } check42(){ - TITLE42="$BLUE 4.2$NORMAL Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored)" - text_title "$TITLE42" + ID42="4.2" + TITLE42="Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored)" + text_title "$ID42" "$TITLE42" 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 $PROFILE --region $regx --output text) if [[ $SG_LIST ]];then @@ -1152,8 +1203,9 @@ check42(){ } check43(){ - TITLE43="$BLUE 4.3$NORMAL Ensure VPC Flow Logging is Enabled in all VPCs (Scored)" - text_title "$TITLE43" + ID43="4.3" + TITLE43="Ensure VPC Flow Logging is Enabled in all VPCs (Scored)" + text_title "$ID43" "$TITLE43" for regx in $REGIONS; do CHECK_FL=$($AWSCLI ec2 describe-flow-logs --profile $PROFILE --region $regx --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].LogGroupName' --output text) if [[ $CHECK_FL ]];then @@ -1167,8 +1219,9 @@ check43(){ } check44(){ - TITLE44="$BLUE 4.4$NORMAL Ensure the default security group of every VPC restricts all traffic (Scored)" - text_title "$TITLE44" + ID44="4.4" + TITLE44="Ensure the default security group of every VPC restricts all traffic (Scored)" + text_title "$ID44" "$TITLE44" for regx in $REGIONS; do CHECK_SGDEFAULT=$($AWSCLI ec2 describe-security-groups --profile $PROFILE --region $regx --filters Name=group-name,Values='default' --query 'SecurityGroups[*].{IpPermissions:IpPermissions,IpPermissionsEgress:IpPermissionsEgress,GroupId:GroupId}' --output text |grep 0.0.0.0) if [[ $CHECK_SGDEFAULT ]];then @@ -1181,8 +1234,9 @@ check44(){ check45(){ #set -xe - TITLE45="$BLUE 4.5$NORMAL Ensure routing tables for VPC peering are \"least access\" (Not Scored)" - text_title "$TITLE45" + ID45="4.5" + TITLE45="Ensure routing tables for VPC peering are \"least access\" (Not Scored)" + text_title "$ID45" "$TITLE45" text_notice "Looking for VPC peering in all regions... " for regx in $REGIONS; do LIST_OF_VPCS_PEERING_CONNECTIONS=$($AWSCLI ec2 describe-vpc-peering-connections --output text --profile $PROFILE --region $regx --query 'VpcPeeringConnections[*].VpcPeeringConnectionId') From acb889a2671566a8115f523fae531e7524ef2ffb Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 15:32:59 -0500 Subject: [PATCH 11/22] Initial CSV Support * rename text output functions to follow convention * send appropriate data to stderr for progress monitoring * send results to stdout in pipe-delimited format * skip banner, etc. when running in CSV mode --- prowler | 516 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 279 insertions(+), 237 deletions(-) diff --git a/prowler b/prowler index 616c7ac1..b6b98737 100755 --- a/prowler +++ b/prowler @@ -104,7 +104,7 @@ if [[ $MODE != "mono" && $MODE != "text" && $MODE != "csv" ]]; then exit 1 fi -if [[ $MODE == "mono" ]]; then +if [[ $MODE == "mono" || $MODE == "csv" ]]; then MONOCHROME=1 fi @@ -241,22 +241,56 @@ fi # AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY_ID} TITLE_ID="" +TITLE_TEXT="CALLER ERROR - UNSET TITLE" ## Output formatting functions -text_ok(){ - echo " $TITLE_ID $OK OK! $NORMAL $@" +textOK(){ + if [[ $MODE == "csv" ]]; then + echo "$PROFILE|$REGION|$TITLE_ID|PASS|$TITLE_TEXT|$1" + else + echo " $TITLE_ID $OK OK! $NORMAL $@" + fi } -text_notice(){ - echo " $TITLE_ID $NOTICE INFO! $@ $NORMAL" +textNotice(){ + if [[ $MODE == "csv" ]]; then + echo "$PROFILE|$REGION|$TITLE_ID|INFO|$TITLE_TEXT|$1" + else + echo " $TITLE_ID $NOTICE INFO! $@ $NORMAL" + fi } -text_warn(){ - echo " $TITLE_ID $BAD WARNING! $@ $NORMAL" +textWarn(){ + if [[ $MODE == "csv" ]]; then + echo "$PROFILE|$REGION|$TITLE_ID|WARNING|$TITLE_TEXT|$1" + else + echo " $TITLE_ID $BAD WARNING! $@ $NORMAL" + fi } -text_title(){ +textTitle(){ TITLE_ID=$1 - echo -e "\n$BLUE $TITLE_ID $NORMAL ${@:2}" + TITLE_TEXT=$2 + + if [[ $3 ]]; then + ITEM_SCORED=$3 + else + ITEM_SCORED="1" + fi + + if [[ $MODE == "csv" ]]; then + >&2 echo "$TITLE_ID $TITLE_TEXT" + else + if [[ $ITEM_SCORED == "1" ]]; then + echo -e "\n$BLUE $TITLE_ID $NORMAL $TITLE_TEXT" + else + echo -e "\n$PURPLE $TITLE_ID $TITLE_TEXT $NORMAL" + fi + fi +} + +printCsvHeader() { + >&2 echo "Pipe Delimited report on stdout; Diagnostics on stderr." + echo "PROFILE|REGION|TITLE_ID|RESULT|TITLE_TEXT|NOTES" } prowlerBanner() { @@ -270,21 +304,26 @@ prowlerBanner() { # Get whoami in AWS, who is the user running this shell script getWhoami(){ - echo "" - echo "This report is being generated using credentials below:" - echo "" - echo -e "AWS-CLI Profile: $NOTICE[$PROFILE]$NORMAL AWS API Region: $NOTICE[$REGION]$NORMAL AWS Filter Region: $NOTICE[${FILTERREGION:-all}]$NORMAL\n" - if [[ $MONOCHROME -eq 1 ]]; then - $AWSCLI sts get-caller-identity --output json --profile $PROFILE --region $REGION | grep ':' + if [[ $MODE == "csv" ]]; then + CALLER_ARN=$($AWSCLI sts get-caller-identity --output json --profile $PROFILE --region $REGION --query "Arn" | tr -d '"') + echo "$PROFILE|$REGION|0.0|INFO|Report generated by user|$CALLER_ARN" else - echo "Caller Identity:" - $AWSCLI sts get-caller-identity --output table --profile $PROFILE --region $REGION echo "" + echo "This report is being generated using credentials below:" + echo "" + echo -e "AWS-CLI Profile: $NOTICE[$PROFILE]$NORMAL AWS API Region: $NOTICE[$REGION]$NORMAL AWS Filter Region: $NOTICE[${FILTERREGION:-all}]$NORMAL\n" + if [[ $MONOCHROME -eq 1 ]]; then + $AWSCLI sts get-caller-identity --output json --profile $PROFILE --region $REGION | grep ':' + else + echo "Caller Identity:" + $AWSCLI sts get-caller-identity --output table --profile $PROFILE --region $REGION + echo "" + fi fi } printCurrentDate(){ - text_notice "Date: $(date)" + textNotice "Date: $(date)" } printColorsCode(){ @@ -295,10 +334,9 @@ printColorsCode(){ # Generate Credential Report genCredReport() { - echo -en '\nGenerating AWS IAM Credential Report...' - until $AWSCLI iam generate-credential-report --output text --query 'State' --profile $PROFILE --region $REGION |grep -m 1 "COMPLETE"; do + textTitle "0.0" "Generating AWS IAM Credential Report..." + until $( $AWSCLI iam generate-credential-report --output text --query 'State' --profile $PROFILE --region $REGION |grep -q -m 1 "COMPLETE") ; do sleep 1 - echo -n "." done } @@ -322,24 +360,24 @@ REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' \ infoReferenceLong(){ # Report review note: - text_notice "For more information:" - text_notice "https://benchmarks.cisecurity.org/tools2/amazon/CIS_Amazon_Web_Services_Foundations_Benchmark_v1.1.0.pdf" - text_notice "For bugs or feedback:" - text_notice "https://github.com/Alfresco/aws-cis-security-benchmark/issues" + textNotice "For more information:" + textNotice "https://benchmarks.cisecurity.org/tools2/amazon/CIS_Amazon_Web_Services_Foundations_Benchmark_v1.1.0.pdf" + textNotice "For bugs or feedback:" + textNotice "https://github.com/Alfresco/aws-cis-security-benchmark/issues" } infoReferenceShort(){ # Report review note: - text_notice "http://bit.ly/2g3PEf7" + textNotice "http://bit.ly/2g3PEf7" } check11(){ ID11="1.1" TITLE11="Avoid the use of the root account (Scored)." COMMAND11=$(cat $TEMP_REPORT_FILE| grep '' | cut -d, -f5,11,16 | sed 's/,/,\ /g') - text_title "$ID11" "$TITLE11" - text_notice "Root account last accessed (password, key_1, key_2): $COMMAND11" + textTitle "$ID11" "$TITLE11" + textNotice "Root account last accessed (password, key_1, key_2): $COMMAND11" } check12(){ @@ -351,20 +389,20 @@ check12(){ 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 }' done) - text_title "$ID12" "$TITLE12" + textTitle "$ID12" "$TITLE12" if [[ $COMMAND12 ]]; then for u in $COMMAND12; do - text_warn "User $u has Password enabled but MFA disabled" + textWarn "User $u has Password enabled but MFA disabled" done else - text_ok "No users found with Password enabled and MFA disabled" + textOK "No users found with Password enabled and MFA disabled" fi } check13(){ ID13="1.3" TITLE13="Ensure credentials unused for 90 days or greater are disabled (Scored)" - text_title "$ID13" "$TITLE13" + textTitle "$ID13" "$TITLE13" 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 COMMAND13=$( @@ -379,14 +417,14 @@ check13(){ DATEUSED=$($AWSCLI iam list-users --query "Users[?UserName=='$i'].PasswordLastUsed" --output text --profile $PROFILE --region $REGION | cut -d'T' -f1) HOWOLDER=$(how_older_from_today $DATEUSED) if [ $HOWOLDER -gt "90" ];then - text_warn "User \"$i\" has not logged in during the last 90 days " + textWarn "User \"$i\" has not logged in during the last 90 days " else - text_ok "User \"$i\" found with credentials used in the last 90 days" + textOK "User \"$i\" found with credentials used in the last 90 days" fi done fi else - text_ok "No users found with password enabled" + textOK "No users found with password enabled" fi } @@ -396,45 +434,45 @@ check14(){ TITLE14="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 }') - text_title "$ID14" "$TITLE14" + textTitle "$ID14" "$TITLE14" C14_NUM_USERS1=0 C14_NUM_USERS2=0 # $(expr $C116_NUM_USERS + 1) if [[ $LIST_OF_USERS_WITH_ACCESS_KEY1 ]]; then - # text_warn "Users with access key 1 older than 90 days:" + # 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 $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 - text_warn " $user has not rotated access key1 in over 90 days " + textWarn " $user has not rotated access key1 in over 90 days " C14_NUM_USERS1 = $(expr $C14_NUM_USERS1 + 1) fi done if [[ $C14_NUM_USERS1 -eq 0 ]]; then - text_ok "No users with access key 1 older than 90 days." + textOK "No users with access key 1 older than 90 days." fi else - text_ok "No users with access key 1." + textOK "No users with access key 1." fi if [[ $LIST_OF_USERS_WITH_ACCESS_KEY2 ]]; then - # text_warn "Users with access key 2 older than 90 days:" + # 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 $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 - text_warn " $user has not rotated access key2. " + textWarn " $user has not rotated access key2. " C14_NUM_USERS2 = $(expr $C14_NUM_USERS2 + 1) fi done if [[ $C14_NUM_USERS2 -eq 0 ]]; then - text_ok "No users with access key 2 older than 90 days." + textOK "No users with access key 2 older than 90 days." fi else - text_ok "No users with access key 2." + textOK "No users with access key 2." fi } @@ -442,11 +480,11 @@ check15(){ ID15="1.5" TITLE15="Ensure IAM password policy requires at least one uppercase letter (Scored)" COMMAND15=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireUppercaseCharacters') # must be true - text_title "$ID15" "$TITLE15" + textTitle "$ID15" "$TITLE15" if [[ $COMMAND15 == "true" ]];then - text_ok "Password Policy requires upper case" + textOK "Password Policy requires upper case" else - text_warn "Password Policy missing upper-case requirement" + textWarn "Password Policy missing upper-case requirement" fi } @@ -454,11 +492,11 @@ check16(){ ID16="1.6" TITLE16="Ensure IAM password policy require at least one lowercase letter (Scored)" COMMAND16=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireLowercaseCharacters') # must be true - text_title "$ID16" "$TITLE16" + textTitle "$ID16" "$TITLE16" if [[ $COMMAND16 == "true" ]];then - text_ok "Password Policy requires lower case" + textOK "Password Policy requires lower case" else - text_warn "Password Policy missing lower-case requirement" + textWarn "Password Policy missing lower-case requirement" fi } @@ -466,11 +504,11 @@ check17(){ ID17="1.7" TITLE17="Ensure IAM password policy require at least one symbol (Scored)" COMMAND17=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireSymbols') # must be true - text_title "$ID17" "$TITLE17" + textTitle "$ID17" "$TITLE17" if [[ $COMMAND17 == "true" ]];then - text_ok "Password Policy requires symbol" + textOK "Password Policy requires symbol" else - text_warn "Password Policy missing symbol requirement" + textWarn "Password Policy missing symbol requirement" fi } @@ -478,11 +516,11 @@ check18(){ ID18="1.8" TITLE18="Ensure IAM password policy require at least one number (Scored)" COMMAND18=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.RequireNumbers') # must be true - text_title "$ID18" "$TITLE18" + textTitle "$ID18" "$TITLE18" if [[ $COMMAND18 == "true" ]];then - text_ok "Password Policy requires number" + textOK "Password Policy requires number" else - text_warn "Password Policy missing number requirement" + textWarn "Password Policy missing number requirement" fi } @@ -490,11 +528,11 @@ check19(){ ID19="1.9" TITLE19="Ensure IAM password policy requires minimum length of 14 or greater (Scored)" COMMAND19=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.MinimumPasswordLength') - text_title "$ID19" "$TITLE19" + textTitle "$ID19" "$TITLE19" if [[ $COMMAND19 -gt "13" ]];then - text_ok "Password Policy requires more than 13 characters" + textOK "Password Policy requires more than 13 characters" else - text_warn "Password Policy missing or weak length requirement" + textWarn "Password Policy missing or weak length requirement" fi } @@ -502,15 +540,15 @@ check110(){ ID110="1.10" TITLE110="Ensure IAM password policy prevents password reuse (Scored)" COMMAND110=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION --query 'PasswordPolicy.PasswordReusePrevention' --output text) - text_title "$ID110" "$TITLE110" + textTitle "$ID110" "$TITLE110" if [[ $COMMAND110 ]];then if [[ $COMMAND110 -gt "23" ]];then - text_ok "Password Policy limits reuse" + textOK "Password Policy limits reuse" else - text_warn "Password Policy has weak reuse requirment (lower than 24)" + textWarn "Password Policy has weak reuse requirment (lower than 24)" fi else - text_warn "Password Policy missing reuse requirement" + textWarn "Password Policy missing reuse requirement" fi } @@ -518,13 +556,13 @@ check111(){ ID111="1.11" TITLE111="Ensure IAM password policy expires passwords within 90 days or less (Scored)" COMMAND111=$($AWSCLI iam get-account-password-policy --profile $PROFILE --region $REGION | grep MaxPasswordAge | awk -F: '{ print $2 }'|sed 's/\ //g'|sed 's/,/ /g') - text_title "$ID111" "$TITLE111" + textTitle "$ID111" "$TITLE111" if [[ $COMMAND111 ]];then if [ $COMMAND111 == "90" ];then - text_ok "Password Policy includes expiration" + textOK "Password Policy includes expiration" fi else - text_warn "Passowrd expiration not set or set greater than 90 days " + textWarn "Passowrd expiration not set or set greater than 90 days " fi } @@ -534,16 +572,16 @@ check112(){ # 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 }') - text_title "$ID112" "$TITLE112" + textTitle "$ID112" "$TITLE112" if [ $ROOTKEY1 == "false" ];then - text_ok "No access key 1 found for root" + textOK "No access key 1 found for root" else - text_warn "Found access key 1 for root " + textWarn "Found access key 1 for root " fi if [ $ROOTKEY2 == "false" ];then - text_ok "No access key 2 found for root" + textOK "No access key 2 found for root" else - text_warn "Found access key 2 for root " + textWarn "Found access key 2 for root " fi } @@ -551,11 +589,11 @@ check113(){ ID113="1.13" TITLE113="Ensure MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary --profile $PROFILE --region $REGION |grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') - text_title "$ID113" "$TITLE113" + textTitle "$ID113" "$TITLE113" if [ $COMMAND113 == "1" ]; then - text_ok "Virtual MFA is enabled for root" + textOK "Virtual MFA is enabled for root" else - text_warn "MFA is not ENABLED for root account " + textWarn "MFA is not ENABLED for root account " fi } @@ -563,16 +601,16 @@ check114(){ ID114="1.14" TITLE114="Ensure hardware MFA is enabled for the root account (Scored)" COMMAND113=$($AWSCLI iam get-account-summary --profile $PROFILE --region $REGION |grep AccountMFAEnabled | awk -F': ' '{ print $2 }'|sed 's/,//') - text_title "$ID114" "$TITLE114" + textTitle "$ID114" "$TITLE114" if [ $COMMAND113 == "1" ]; then COMMAND114=$($AWSCLI iam list-virtual-mfa-devices --profile $PROFILE --region $REGION --query 'VirtualMFADevices' --output text|grep :root |wc -l) if [ $COMMAND114 == "1" ]; then - text_ok "Virtual MFA is enabled for root" + textOK "Virtual MFA is enabled for root" else - text_ok "Hardware MFA is enabled for root " + textOK "Hardware MFA is enabled for root " fi else - text_warn "MFA is not ENABLED for root account " + textWarn "MFA is not ENABLED for root account " fi } @@ -580,27 +618,27 @@ check115(){ ID115="1.15" TITLE115="Ensure security questions are registered in the AWS account (Not Scored)" # No command available - text_title "$ID115" "$TITLE115" - text_notice "No command available for check 1.15 " - text_notice "Login to the AWS Console as root, click on the Account " - text_notice "Name -> My Account -> Configure Security Challenge Questions " + textTitle "$ID115" "$TITLE115" "0" + textNotice "No command available for check 1.15 " + textNotice "Login to the AWS Console as root, click on the Account " + textNotice "Name -> My Account -> Configure Security Challenge Questions " } check116(){ ID116="1.16" TITLE116="Ensure IAM policies are attached only to groups or roles (Scored)" - text_title "$ID116" "$TITLE116" + textTitle "$ID116" "$TITLE116" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text --profile $PROFILE --region $REGION) C116_NUM_USERS=0 for user in $LIST_USERS;do USER_POLICY=$($AWSCLI iam list-attached-user-policies --output text --profile $PROFILE --region $REGION --user-name $user) if [[ $USER_POLICY ]]; then - text_warn "$user has policy directly attached " + textWarn "$user has policy directly attached " C116_NUM_USERS=$(expr $C116_NUM_USERS + 1) fi done if [[ $C116_NUM_USERS -eq 0 ]]; then - text_ok "No policies attached to users." + textOK "No policies attached to users." fi } @@ -608,26 +646,26 @@ check117(){ ID117="1.17" TITLE117="Enable detailed billing (Scored)" # No command available - text_title "$ID117" "$TITLE117" - text_notice "No command available for check 1.17 " - text_notice "See section 1.17 on the CIS Benchmark guide for details " + textTitle "$ID117" "$TITLE117" + textNotice "No command available for check 1.17 " + textNotice "See section 1.17 on the CIS Benchmark guide for details " infoReferenceShort } check118(){ ID118="1.18" TITLE118="Ensure IAM Master and IAM Manager roles are active (Scored)" - text_title "$ID118" "$TITLE118" + textTitle "$ID118" "$TITLE118" FINDMASTERANDMANAGER=$($AWSCLI iam list-roles --profile $PROFILE --region $REGION --query "Roles[*].{RoleName:RoleName}" --output text | grep -E 'Master|Manager'| tr '\n' ' ') if [[ $FINDMASTERANDMANAGER ]];then - text_notice "Found next roles as possible IAM Master and IAM Manager candidates: " - text_notice "$FINDMASTERANDMANAGER " - text_notice "run the commands below to check their policies with section 1.18 in the guide..." + textNotice "Found next roles as possible IAM Master and IAM Manager candidates: " + textNotice "$FINDMASTERANDMANAGER " + textNotice "run the commands below to check their policies with section 1.18 in the guide..." for role in $FINDMASTERANDMANAGER;do # find inline policies in found roles INLINEPOLICIES=$($AWSCLI iam list-role-policies --role-name $role --profile $PROFILE --region $REGION --query "PolicyNames[*]" --output text) for policy in $INLINEPOLICIES;do - text_notice "$AWSCLI iam get-role-policy --role-name $role --policy-name $policy --profile $PROFILE --region $REGION" + textNotice "$AWSCLI iam get-role-policy --role-name $role --policy-name $policy --profile $PROFILE --region $REGION" done # find attached policies in found roles ATTACHEDPOLICIES=$($AWSCLI iam list-attached-role-policies --role-name $role --profile $PROFILE --region $REGION --query "AttachedPolicies[*]" --output text) @@ -636,7 +674,7 @@ check118(){ done done else - text_warn "IAM Master and IAM Manager roles not found" + textWarn "IAM Master and IAM Manager roles not found" fi } @@ -644,9 +682,9 @@ check119(){ ID119="1.19" TITLE119="Maintain current contact details (Scored)" # No command available - text_title "$ID119" "$TITLE119" - text_notice "No command available for check 1.19 " - text_notice "See section 1.19 on the CIS Benchmark guide for details " + textTitle "$ID119" "$TITLE119" + textNotice "No command available for check 1.19 " + textNotice "See section 1.19 on the CIS Benchmark guide for details " infoReferenceShort } @@ -654,73 +692,73 @@ check120(){ ID120="1.20" TITLE120="Ensure security contact information is registered (Scored)" # No command available - text_title "$ID120" "$TITLE120" - text_notice "No command available for check 1.20 " - text_notice "See section 1.20 on the CIS Benchmark guide for details " + textTitle "$ID120" "$TITLE120" + textNotice "No command available for check 1.20 " + textNotice "See section 1.20 on the CIS Benchmark guide for details " infoReferenceShort } check121(){ ID121="1.21" TITLE121="Ensure IAM instance roles are used for AWS resource access from instances (Not Scored)" - text_title "$ID121" "$TITLE121" - text_notice "No command available for check 1.21 " - text_notice "See section 1.21 on the CIS Benchmark guide for details " + textTitle "$ID121" "$TITLE121" "0" + textNotice "No command available for check 1.21 " + textNotice "See section 1.21 on the CIS Benchmark guide for details " infoReferenceShort } check122(){ ID122="1.22" TITLE122="Ensure a support role has been created to manage incidents with AWS Support (Scored)" - text_title "$ID122" "$TITLE122" + textTitle "$ID122" "$TITLE122" SUPPORTPOLICYARN=$($AWSCLI iam list-policies --query "Policies[?PolicyName == 'AWSSupportAccess'].Arn" --profile $PROFILE --region $REGION --output text) if [[ $SUPPORTPOLICYARN ]];then for policyarn in $SUPPORTPOLICYARN;do POLICYTOSHOW=$($AWSCLI iam list-entities-for-policy --policy-arn $SUPPORTPOLICYARN --profile $PROFILE --region $REGION --output text) if [[ $POLICYTOSHOW ]];then - text_ok "Support Policy attached to $POLICYTOSHOW" - text_notice "Make sure your team can create a Support case with AWS " + textOK "Support Policy attached to $POLICYTOSHOW" + textNotice "Make sure your team can create a Support case with AWS " else - text_warn "Support Policy not applied to any Group, User or Role " + textWarn "Support Policy not applied to any Group, User or Role " fi done else - text_warn "No Support Policy found" + textWarn "No Support Policy found" fi } 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)" - text_title "$ID123" "$TITLE123" + textTitle "$ID123" "$TITLE123" "0" LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text --profile $PROFILE --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) if [[ $LIST_USERS_KEY1_ACTIVE ]]; then - # text_notice "List of users with Access Key 1 never used:" - text_notice "Users who never used Access Key 1: $LIST_USERS_KEY1_ACTIVE" + # textNotice "List of users with Access Key 1 never used:" + textNotice "Users who never used Access Key 1: $LIST_USERS_KEY1_ACTIVE" else - text_ok "No users found with Access Key 1 never used" + 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) if [[ $LIST_USERS_KEY2_ACTIVE ]]; then - # text_notice "List of users with Access Key 2 never used:" - text_notice "Users who never used Access Key 2: $LIST_USERS_KEY2_ACTIVE" + # textNotice "List of users with Access Key 2 never used:" + textNotice "Users who never used Access Key 2: $LIST_USERS_KEY2_ACTIVE" else - text_ok "No users found with Access Key 2 never used" + textOK "No users found with Access Key 2 never used" fi } check124(){ ID124="1.24" TITLE124="Ensure IAM policies that allow full \"*:*\" administrative privileges are not created (Scored)" - text_title "$ID124" "$TITLE124" + textTitle "$ID124" "$TITLE124" LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text --profile $PROFILE --region $REGION|grep 'arn:aws:iam::[0-9]\{12\}:'|awk '{ print $2 }') if [[ $LIST_CUSTOM_POLICIES ]]; then - text_notice "Looking for custom policies: (skipping default policies, it may take few seconds...)" + 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 $PROFILE --region $REGION --query 'Policies[*].[Arn,DefaultVersionId]' --output text|grep -w $policy |awk '{ 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 $PROFILE --region $REGION) @@ -729,79 +767,79 @@ check124(){ fi done if [[ $POLICIES_ALLOW_LIST ]]; then - text_notice "List of custom policies: " + textNotice "List of custom policies: " for policy in $POLICIES_ALLOW_LIST; do - text_notice "Policy $policy allows \"*:*\"" + textNotice "Policy $policy allows \"*:*\"" done else - text_ok "No custom policy found that allow full \"*:*\" administrative privileges" + textOK "No custom policy found that allow full \"*:*\" administrative privileges" fi else - text_ok "No custom policies found" + textOK "No custom policies found" fi } check21(){ ID21="2.1" TITLE21="Ensure CloudTrail is enabled in all regions (Scored)" - text_title "$ID21" "$TITLE21" + textTitle "$ID21" "$TITLE21" LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].Name' --output text) if [[ $LIST_OF_TRAILS ]];then for trail in $LIST_OF_TRAILS;do MULTIREGION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].IsMultiRegionTrail' --output text --trail-name-list $trail) if [[ $MULTIREGION_TRAIL_STATUS == 'False' ]];then - text_warn "$trail trail in $REGION is not enabled in multi region mode" + textWarn "$trail trail in $REGION is not enabled in multi region mode" else - text_ok "$trail trail in $REGION is enabled for all regions" + textOK "$trail trail in $REGION is enabled for all regions" fi done else - text_warn "No CloudTrail trails found!" + textWarn "No CloudTrail trails found!" fi } check22(){ ID22="2.2" TITLE22="Ensure CloudTrail log file validation is enabled (Scored)" - text_title "$ID22" "$TITLE22" + textTitle "$ID22" "$TITLE22" LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].Name' --output text) if [[ $LIST_OF_TRAILS ]];then for trail in $LIST_OF_TRAILS;do LOGFILEVALIDATION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].LogFileValidationEnabled' --output text --trail-name-list $trail) if [[ $LOGFILEVALIDATION_TRAIL_STATUS == 'False' ]];then - text_warn "$trail trail in $REGION has not log file validation enabled" + textWarn "$trail trail in $REGION has not log file validation enabled" else - text_ok "$trail trail in $REGION has log file validation enabled" + textOK "$trail trail in $REGION has log file validation enabled" fi done else - text_warn "No CloudTrail trails found!" + textWarn "No CloudTrail trails found!" fi } check23(){ ID23="2.3" TITLE23="Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)" - text_title "$ID23" "$TITLE23" + textTitle "$ID23" "$TITLE23" CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION) if [[ $CLOUDTRAILBUCKET ]];then for bucket in $CLOUDTRAILBUCKET;do CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $bucket --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --profile $PROFILE --region $REGION --output text) if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]];then - text_warn "check your $bucket CloudTrail bucket ACL and Policy!" + textWarn "check your $bucket CloudTrail bucket ACL and Policy!" else - text_ok "Bucket $bucket is set correctly" + textOK "Bucket $bucket is set correctly" fi done else - text_warn "No CloudTrail bucket found!" + textWarn "No CloudTrail bucket found!" fi } check24(){ ID24="2.4" TITLE24="Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)" - text_title "$ID24" "$TITLE24" + textTitle "$ID24" "$TITLE24" TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].{Name:Name, HomeRegion:HomeRegion}' --output text | tr "\t" ',') if [[ $TRAILS_AND_REGIONS ]];then for reg_trail in $TRAILS_AND_REGIONS;do @@ -809,32 +847,32 @@ check24(){ TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1) LATESTDELIVERY_TIMESTAMP=$($AWSCLI cloudtrail get-trail-status --name $trail --profile $PROFILE --region $TRAIL_REGION --query 'LatestCloudWatchLogsDeliveryTime' --output text|grep -v None) if [[ ! $LATESTDELIVERY_TIMESTAMP ]];then - text_warn "$trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)" + textWarn "$trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)" else LATESTDELIVERY_DATE=$(timestamp_to_date $LATESTDELIVERY_TIMESTAMP) HOWOLDER=$(how_older_from_today $LATESTDELIVERY_DATE) if [ $HOWOLDER -gt "1" ];then - text_warn "$trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)" + textWarn "$trail trail is not logging in the last 24h or not configured (it is in $TRAIL_REGION)" else - text_ok "$trail trail has been logging during the last 24h (it is in $TRAIL_REGION)" + textOK "$trail trail has been logging during the last 24h (it is in $TRAIL_REGION)" fi fi done else - text_warn "No CloudTrail trails found!" + textWarn "No CloudTrail trails found!" fi } check25(){ ID25="2.5" TITLE25="Ensure AWS Config is enabled in all regions (Scored)" - text_title "$ID25" "$TITLE25" + textTitle "$ID25" "$TITLE25" for regx in $REGIONS; do CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status --profile $PROFILE --region $regx | grep "recorder: ON") if [[ $CHECK_AWSCONFIG_STATUS ]];then - text_ok "Region $regx has AWS Config recorder: ON " + textOK "Region $regx has AWS Config recorder: ON " else - text_warn "Region $regx has AWS Config disabled or not configured" + textWarn "Region $regx has AWS Config disabled or not configured" fi done } @@ -842,45 +880,45 @@ check25(){ check26(){ ID26="2.6" TITLE26="Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" - text_title "$ID26" "$TITLE26" + textTitle "$ID26" "$TITLE26" CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION) if [[ $CLOUDTRAILBUCKET ]];then for bucket in $CLOUDTRAILBUCKET;do CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket --profile $PROFILE --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None) if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then - text_ok "Bucket access logging enabled in $bucket" + textOK "Bucket access logging enabled in $bucket" else - text_warn "access logging is not enabled in $bucket CloudTrail S3 bucket!" + textWarn "access logging is not enabled in $bucket CloudTrail S3 bucket!" fi done else - text_warn "CloudTrail bucket not found!" + textWarn "CloudTrail bucket not found!" fi } check27(){ ID27="2.7" TITLE27="Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)" - text_title "$ID27" "$TITLE27" + textTitle "$ID27" "$TITLE27" CLOUDTRAILNAME=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].Name' --output text --profile $PROFILE --region $REGION) if [[ $CLOUDTRAILNAME ]];then for trail in $CLOUDTRAILNAME;do CLOUDTRAILENC_ENABLED=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --trail $trail --query 'trailList[*].KmsKeyId' --output text) if [[ $CLOUDTRAILENC_ENABLED ]];then - text_ok "KMS key found for $trail" + textOK "KMS key found for $trail" else - text_warn "encryption is not enabled in your CloudTrail trail $trail, KMS key not found!" + textWarn "encryption is not enabled in your CloudTrail trail $trail, KMS key not found!" fi done else - text_warn "CloudTrail bucket doesn't exist!" + textWarn "CloudTrail bucket doesn't exist!" fi } check28(){ ID28="2.8" TITLE28="Ensure rotation for customer created CMKs is enabled (Scored)" - text_title "$ID28" "$TITLE28" + textTitle "$ID28" "$TITLE28" for regx in $REGIONS; do CHECK_KMS_KEYLIST=$($AWSCLI kms list-keys --profile $PROFILE --region $regx --output text --query 'Keys[*].KeyId') if [[ $CHECK_KMS_KEYLIST ]];then @@ -888,22 +926,22 @@ check28(){ for key in $CHECK_KMS_KEYLIST_NO_DEFAULT; do CHECK_KMS_KEY_TYPE=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --region $regx --query 'KeyMetadata.Origin' | sed 's/["]//g') if [[ $CHECK_KMS_KEY_TYPE == "EXTERNAL" ]];then - text_ok "Key $key in Region $regx Customer Uploaded Key Material." + textOK "Key $key in Region $regx Customer Uploaded Key Material." else CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key --profile $PROFILE --region $regx --output text) #CHECK_KMS_DEFAULT_KEY=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --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 - text_ok "Key $key in Region $regx is set correctly" + textOK "Key $key in Region $regx is set correctly" elif [[ $CHECK_KMS_KEY_ROTATION == "False" && $CHECK_KMS_DEFAULT_KEY ]];then - text_notice "Region $regx key $key is an AWS default master key and cannot be deleted nor modified." + textNotice "Region $regx key $key is an AWS default master key and cannot be deleted nor modified." else - text_warn "Key $key in Region $regx is not set to rotate!!!" + textWarn "Key $key in Region $regx is not set to rotate!!!" fi fi done else - text_notice "Region $regx doesn't have encryption keys " + textNotice "Region $regx doesn't have encryption keys " fi done } @@ -911,245 +949,245 @@ check28(){ check31(){ ID31="3.1" TITLE31="Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" - text_title "$ID31" "$TITLE31" + textTitle "$ID31" "$TITLE31" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep AccessDenied) if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters for Access Denied enabled" + textOK "CloudWatch group found, and metric filters for Access Denied enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check32(){ ID32="3.2" TITLE32="Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" - text_title "$ID32" "$TITLE32" + textTitle "$ID32" "$TITLE32" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'userIdentity.sessionContext.attributes.mfaAuthenticated.*true') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters for sign-in Console without MFA enabled" + textOK "CloudWatch group found, and metric filters for sign-in Console without MFA enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check33(){ ID33="3.3" TITLE33="Ensure a log metric filter and alarm exist for usage of root account (Scored)" - text_title "$ID33" "$TITLE33" + textTitle "$ID33" "$TITLE33" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters for usage of root account enabled" + textOK "CloudWatch group found, and metric filters for usage of root account enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check34(){ ID34="3.4" TITLE34="Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" - text_title "$ID34" "$TITLE34" + textTitle "$ID34" "$TITLE34" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --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 - text_ok "CloudWatch group found, and metric filters for IAM policy changes enabled" + textOK "CloudWatch group found, and metric filters for IAM policy changes enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check35(){ ID35="3.5" TITLE35="Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" - text_title "$ID35" "$TITLE35" + textTitle "$ID35" "$TITLE35" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters for CloudTrail configuration changes enabled" + textOK "CloudWatch group found, and metric filters for CloudTrail configuration changes enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check36(){ ID36="3.6" TITLE36="Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" - text_title "$ID36" "$TITLE36" + textTitle "$ID36" "$TITLE36" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters for usage of root account enabled" + textOK "CloudWatch group found, and metric filters for usage of root account enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check37(){ ID37="3.7" TITLE37="Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" - text_title "$ID37" "$TITLE37" + textTitle "$ID37" "$TITLE37" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found, and metric filters enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check38(){ ID38="3.8" TITLE38="Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" - text_title "$ID38" "$TITLE38" + textTitle "$ID38" "$TITLE38" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found, and metric filters enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check39(){ ID39="3.9" TITLE39="Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" - text_title "$ID39" "$TITLE39" + textTitle "$ID39" "$TITLE39" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found, and metric filters enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check310(){ ID310="3.10" TITLE310="Ensure a log metric filter and alarm exist for security group changes (Scored)" - text_title "$ID310" "$TITLE310" + textTitle "$ID310" "$TITLE310" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found, and metric filters enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check311(){ ID311="3.11" TITLE311="Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" - text_title "$ID311" "$TITLE311" + textTitle "$ID311" "$TITLE311" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found, and metric filters enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check312(){ ID312="3.12" TITLE312="Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" - text_title "$ID312" "$TITLE312" + textTitle "$ID312" "$TITLE312" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found, and metric filters enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check313(){ ID313="3.13" TITLE313="Ensure a log metric filter and alarm exist for route table changes (Scored)" - text_title "$ID313" "$TITLE313" + textTitle "$ID313" "$TITLE313" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found, and metric filters enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check314(){ ID314="3.14" TITLE314="Ensure a log metric filter and alarm exist for VPC changes (Scored)" - text_title "$ID314" "$TITLE314" + textTitle "$ID314" "$TITLE314" CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --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 $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink') if [[ $METRICFILTER_SET ]];then - text_ok "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found, and metric filters enabled" else - text_warn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found, but no metric filters or alarms associated" fi else - text_warn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found, no metric filters or alarms associated" fi } check315(){ ID315="3.15" TITLE315="Ensure appropriate subscribers to each SNS topic (Not Scored)" - text_title "$ID315" "$TITLE315" + textTitle "$ID315" "$TITLE315" "0" for regx in $REGIONS; do TOPICS_LIST=$($AWSCLI sns list-topics --profile $PROFILE --region $regx --output text --query 'Topics[*].TopicArn') if [[ $TOPICS_LIST ]];then @@ -1157,15 +1195,15 @@ check315(){ CHECK_TOPIC_LIST=$($AWSCLI sns list-subscriptions-by-topic --topic-arn $topic --profile $PROFILE --region $regx --query 'Subscriptions[*].{Endpoint:Endpoint,Protocol:Protocol}' --output text --max-items $MAXITEMS | grep -v "None") if [[ $CHECK_TOPIC_LIST ]]; then TOPIC_SHORT=$(echo $topic | awk -F: '{ print $7 }') - text_notice "Region $regx with Topic $TOPIC_SHORT: " - text_notice "- Suscription: $CHECK_TOPIC_LIST " + textNotice "Region $regx with Topic $TOPIC_SHORT: " + textNotice "- Suscription: $CHECK_TOPIC_LIST " else - text_warn "No suscription found in: Region $regx and Topic $topic " - text_warn " - Region $regx and Topic $topic " + textWarn "No suscription found in: Region $regx and Topic $topic " + textWarn " - Region $regx and Topic $topic " fi done else - text_notice "Region $regx doesn't have topics " + textNotice "Region $regx doesn't have topics " fi done } @@ -1173,15 +1211,15 @@ check315(){ check41(){ ID41="4.1" TITLE41="Ensure no security groups allow ingress from 0.0.0.0/0 to port 22 (Scored)" - text_title "$ID41" "$TITLE41" + textTitle "$ID41" "$TITLE41" 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`].{GroupName: GroupName}' --profile $PROFILE --region $regx --output text) if [[ $SG_LIST ]];then for SG in $SG_LIST;do - text_warn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx " + textWarn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx " done else - text_ok "No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0 " + textOK "No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0 " fi done } @@ -1189,15 +1227,15 @@ check41(){ check42(){ ID42="4.2" TITLE42="Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389 (Scored)" - text_title "$ID42" "$TITLE42" + textTitle "$ID42" "$TITLE42" 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 $PROFILE --region $regx --output text) if [[ $SG_LIST ]];then for SG in $SG_LIST;do - text_warn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx " + textWarn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx " done else - text_ok "No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0 " + textOK "No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0 " fi done } @@ -1205,15 +1243,15 @@ check42(){ check43(){ ID43="4.3" TITLE43="Ensure VPC Flow Logging is Enabled in all VPCs (Scored)" - text_title "$ID43" "$TITLE43" + textTitle "$ID43" "$TITLE43" for regx in $REGIONS; do CHECK_FL=$($AWSCLI ec2 describe-flow-logs --profile $PROFILE --region $regx --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].LogGroupName' --output text) if [[ $CHECK_FL ]];then for FL in $CHECK_FL;do - text_ok "VPCFlowLog is enabled for LogGroupName: $FL in Region $regx " + textOK "VPCFlowLog is enabled for LogGroupName: $FL in Region $regx " done else - text_warn "No VPCFlowLog has been found in Region $regx " + textWarn "No VPCFlowLog has been found in Region $regx " fi done } @@ -1221,13 +1259,13 @@ check43(){ check44(){ ID44="4.4" TITLE44="Ensure the default security group of every VPC restricts all traffic (Scored)" - text_title "$ID44" "$TITLE44" + textTitle "$ID44" "$TITLE44" for regx in $REGIONS; do CHECK_SGDEFAULT=$($AWSCLI ec2 describe-security-groups --profile $PROFILE --region $regx --filters Name=group-name,Values='default' --query 'SecurityGroups[*].{IpPermissions:IpPermissions,IpPermissionsEgress:IpPermissionsEgress,GroupId:GroupId}' --output text |grep 0.0.0.0) if [[ $CHECK_SGDEFAULT ]];then - text_warn "Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region $regx" + textWarn "Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region $regx" else - text_ok "No Default Security Groups open to 0.0.0.0 found in Region $regx " + textOK "No Default Security Groups open to 0.0.0.0 found in Region $regx " fi done } @@ -1236,12 +1274,12 @@ check45(){ #set -xe ID45="4.5" TITLE45="Ensure routing tables for VPC peering are \"least access\" (Not Scored)" - text_title "$ID45" "$TITLE45" - text_notice "Looking for VPC peering in all regions... " + textTitle "$ID45" "$TITLE45" "0" + textNotice "Looking for VPC peering in all regions... " for regx in $REGIONS; do LIST_OF_VPCS_PEERING_CONNECTIONS=$($AWSCLI ec2 describe-vpc-peering-connections --output text --profile $PROFILE --region $regx --query 'VpcPeeringConnections[*].VpcPeeringConnectionId') if [[ $LIST_OF_VPCS_PEERING_CONNECTIONS ]];then - text_notice "$regx: $LIST_OF_VPCS_PEERING_CONNECTIONS, review its routing tables " + textNotice "$regx: $LIST_OF_VPCS_PEERING_CONNECTIONS, review its routing tables " #LIST_OF_VPCS=$($AWSCLI ec2 describe-vpcs --profile $PROFILE --region $regx --query 'Vpcs[*].VpcId' --output text) #aws ec2 describe-route-tables --filter "Name=vpc-id,Values=vpc-0213e864" --query "RouteTables[*].{RouteTableId:RouteTableId, VpcId:VpcId, Routes:Routes, AssociatedSubnets:Associations[*].SubnetId}" --profile $PROFILE --region $regx # for vpc in $LIST_OF_VPCS; do @@ -1249,7 +1287,7 @@ check45(){ # done #echo $VPCS_WITH_PEERING else - text_ok "$regx: No VPC peering found " + textOK "$regx: No VPC peering found " fi done } @@ -1326,7 +1364,7 @@ callCheck(){ check41;check42;check43;check44;check45 ;; * ) - text_warn "ERROR! Use a valid check name (i.e. check41)\n"; + textWarn "ERROR! Use a valid check name (i.e. check41)\n"; esac cleanTemp exit @@ -1336,10 +1374,14 @@ callCheck(){ ### All functions defined above ... run the workflow -prowlerBanner -printCurrentDate +if [[ $MODE != "csv" ]]; then + prowlerBanner + printCurrentDate + printColorsCode +else + printCsvHeader +fi getWhoami -printColorsCode genCredReport saveReport From 2f16ee4e6ea2608431c06841df4eafbf7bc67e52 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 15:40:28 -0500 Subject: [PATCH 12/22] add SCORED column to CSV output; handle section tiles properly --- prowler | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/prowler b/prowler index b6b98737..04ebfdaf 100755 --- a/prowler +++ b/prowler @@ -245,7 +245,7 @@ TITLE_TEXT="CALLER ERROR - UNSET TITLE" ## Output formatting functions textOK(){ if [[ $MODE == "csv" ]]; then - echo "$PROFILE|$REGION|$TITLE_ID|PASS|$TITLE_TEXT|$1" + echo "$PROFILE|$REGION|$TITLE_ID|PASS|$ITEM_SCORED|$TITLE_TEXT|$1" else echo " $TITLE_ID $OK OK! $NORMAL $@" fi @@ -253,7 +253,7 @@ textOK(){ textNotice(){ if [[ $MODE == "csv" ]]; then - echo "$PROFILE|$REGION|$TITLE_ID|INFO|$TITLE_TEXT|$1" + echo "$PROFILE|$REGION|$TITLE_ID|INFO|$ITEM_SCORED|$TITLE_TEXT|$1" else echo " $TITLE_ID $NOTICE INFO! $@ $NORMAL" fi @@ -261,7 +261,7 @@ textNotice(){ textWarn(){ if [[ $MODE == "csv" ]]; then - echo "$PROFILE|$REGION|$TITLE_ID|WARNING|$TITLE_TEXT|$1" + echo "$PROFILE|$REGION|$TITLE_ID|WARNING|$ITEM_SCORED|$TITLE_TEXT|$1" else echo " $TITLE_ID $BAD WARNING! $@ $NORMAL" fi @@ -290,7 +290,7 @@ textTitle(){ printCsvHeader() { >&2 echo "Pipe Delimited report on stdout; Diagnostics on stderr." - echo "PROFILE|REGION|TITLE_ID|RESULT|TITLE_TEXT|NOTES" + echo "PROFILE|REGION|TITLE_ID|RESULT|SCORED|TITLE_TEXT|NOTES" } prowlerBanner() { @@ -1388,8 +1388,8 @@ saveReport callCheck -TITLE1="$BLUE 1 Identity and Access Management *********************************$NORMAL" -echo -e "\n\n$TITLE1" +TITLE1="Identity and Access Management ****************************************" +textTitle "1" "$TITLE1" check11 check12 check13 @@ -1415,8 +1415,8 @@ check122 check123 check124 -TITLE2="$BLUE 2 Logging ********************************************************$NORMAL" -echo -e "\n\n$TITLE2" +TITLE2="Logging ***************************************************************" +textTitle "2" "$TITLE2" check21 check22 check23 @@ -1426,8 +1426,8 @@ check26 check27 check28 -TITLE3="$BLUE 3 Monitoring *****************************************************" -echo -e "\n\n$TITLE3" +TITLE3="Monitoring ************************************************************" +textTitle "3" "$TITLE3" # 3 Monitoring check commands / Mostly covered by SecurityMonkey check31 check32 @@ -1445,8 +1445,8 @@ check313 check314 check315 -TITLE4="$BLUE 4 Networking **************************************************$NORMAL" -echo -e "\n\n$TITLE4" +TITLE4="Networking ************************************************************" +textTitle "4" "$TITLE4" check41 check42 check43 From 9a44414a8eed47a44e485dda8d0553917f6908d0 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 16:05:27 -0500 Subject: [PATCH 13/22] more CSV tweaks * handle multi-region tests cleanly * clean up report generation info --- prowler | 79 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/prowler b/prowler index 04ebfdaf..7ce453a1 100755 --- a/prowler +++ b/prowler @@ -245,25 +245,40 @@ TITLE_TEXT="CALLER ERROR - UNSET TITLE" ## Output formatting functions textOK(){ if [[ $MODE == "csv" ]]; then - echo "$PROFILE|$REGION|$TITLE_ID|PASS|$ITEM_SCORED|$TITLE_TEXT|$1" + if [[ $2 ]]; then + REPREGION=$2 + else + REPREGION=$REGION + fi + echo "$PROFILE|$REPREGION|$TITLE_ID|PASS|$ITEM_SCORED|$TITLE_TEXT|$1" else - echo " $TITLE_ID $OK OK! $NORMAL $@" + echo " $TITLE_ID $OK OK! $NORMAL $1" fi } textNotice(){ if [[ $MODE == "csv" ]]; then - echo "$PROFILE|$REGION|$TITLE_ID|INFO|$ITEM_SCORED|$TITLE_TEXT|$1" + if [[ $2 ]]; then + REPREGION=$2 + else + REPREGION=$REGION + fi + echo "$PROFILE|$REPREGION|$TITLE_ID|INFO|$ITEM_SCORED|$TITLE_TEXT|$1" else - echo " $TITLE_ID $NOTICE INFO! $@ $NORMAL" + echo " $TITLE_ID $NOTICE INFO! $1 $NORMAL" fi } textWarn(){ if [[ $MODE == "csv" ]]; then - echo "$PROFILE|$REGION|$TITLE_ID|WARNING|$ITEM_SCORED|$TITLE_TEXT|$1" + if [[ $2 ]]; then + REPREGION=$2 + else + REPREGION=$REGION + fi + echo "$PROFILE|$REPREGION|$TITLE_ID|WARNING|$ITEM_SCORED|$TITLE_TEXT|$1" else - echo " $TITLE_ID $BAD WARNING! $@ $NORMAL" + echo " $TITLE_ID $BAD WARNING! $1 $NORMAL" fi } @@ -289,7 +304,8 @@ textTitle(){ } printCsvHeader() { - >&2 echo "Pipe Delimited report on stdout; Diagnostics on stderr." + >&2 echo "Generateing pipe delimited report on stdout; Diagnostics on stderr." + >&2 echo " " echo "PROFILE|REGION|TITLE_ID|RESULT|SCORED|TITLE_TEXT|NOTES" } @@ -306,7 +322,8 @@ prowlerBanner() { getWhoami(){ if [[ $MODE == "csv" ]]; then CALLER_ARN=$($AWSCLI sts get-caller-identity --output json --profile $PROFILE --region $REGION --query "Arn" | tr -d '"') - echo "$PROFILE|$REGION|0.0|INFO|Report generated by user|$CALLER_ARN" + textTitle "0.0" "Show report generation info" + textNotice "ARN: $CALLER_ARN TIMESTAMP: $(date -Is)" else echo "" echo "This report is being generated using credentials below:" @@ -870,9 +887,9 @@ check25(){ for regx in $REGIONS; do CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status --profile $PROFILE --region $regx | grep "recorder: ON") if [[ $CHECK_AWSCONFIG_STATUS ]];then - textOK "Region $regx has AWS Config recorder: ON " + textOK "Region $regx has AWS Config recorder: ON" "$regx" else - textWarn "Region $regx has AWS Config disabled or not configured" + textWarn "Region $regx has AWS Config disabled or not configured" "$regx" fi done } @@ -926,22 +943,22 @@ check28(){ for key in $CHECK_KMS_KEYLIST_NO_DEFAULT; do CHECK_KMS_KEY_TYPE=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --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." + 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 $PROFILE --region $regx --output text) #CHECK_KMS_DEFAULT_KEY=$($AWSCLI kms describe-key --key-id $key --profile $PROFILE --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." + 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!!!" + textWarn "Key $key in Region $regx is not set to rotate!!!" "$regx" fi fi done else - textNotice "Region $regx doesn't have encryption keys " + textNotice "Region $regx doesn't have encryption keys" "$regx" fi done } @@ -1195,15 +1212,15 @@ check315(){ CHECK_TOPIC_LIST=$($AWSCLI sns list-subscriptions-by-topic --topic-arn $topic --profile $PROFILE --region $regx --query 'Subscriptions[*].{Endpoint:Endpoint,Protocol:Protocol}' --output text --max-items $MAXITEMS | grep -v "None") if [[ $CHECK_TOPIC_LIST ]]; then TOPIC_SHORT=$(echo $topic | awk -F: '{ print $7 }') - textNotice "Region $regx with Topic $TOPIC_SHORT: " - textNotice "- Suscription: $CHECK_TOPIC_LIST " + textNotice "Region $regx with Topic $TOPIC_SHORT:" "$regx" + textNotice "- Suscription: $CHECK_TOPIC_LIST" "$regx" else - textWarn "No suscription found in: Region $regx and Topic $topic " - textWarn " - Region $regx and Topic $topic " + textWarn "No suscription found in: Region $regx and Topic $topic" "$regx" + textWarn " - Region $regx and Topic $topic" "$regx" fi done else - textNotice "Region $regx doesn't have topics " + textNotice "Region $regx doesn't have topics" "$regx" fi done } @@ -1216,10 +1233,10 @@ check41(){ 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`].{GroupName: GroupName}' --profile $PROFILE --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 " + textWarn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx" "$regx" done else - textOK "No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0 " + textOK "No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0" "$regx" fi done } @@ -1232,10 +1249,10 @@ check42(){ 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 $PROFILE --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 " + textWarn "Found Security Group: $SG open to 0.0.0.0/0 in Region $regx" "$regx" done else - textOK "No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0 " + textOK "No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0" "$regx" fi done } @@ -1248,10 +1265,10 @@ check43(){ CHECK_FL=$($AWSCLI ec2 describe-flow-logs --profile $PROFILE --region $regx --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].LogGroupName' --output text) if [[ $CHECK_FL ]];then for FL in $CHECK_FL;do - textOK "VPCFlowLog is enabled for LogGroupName: $FL in Region $regx " + textOK "VPCFlowLog is enabled for LogGroupName: $FL in Region $regx" "$regx" done else - textWarn "No VPCFlowLog has been found in Region $regx " + textWarn "No VPCFlowLog has been found in Region $regx" "$regx" fi done } @@ -1263,9 +1280,9 @@ check44(){ for regx in $REGIONS; do CHECK_SGDEFAULT=$($AWSCLI ec2 describe-security-groups --profile $PROFILE --region $regx --filters Name=group-name,Values='default' --query 'SecurityGroups[*].{IpPermissions:IpPermissions,IpPermissionsEgress:IpPermissionsEgress,GroupId:GroupId}' --output text |grep 0.0.0.0) if [[ $CHECK_SGDEFAULT ]];then - textWarn "Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region $regx" + textWarn "Default Security Groups found that allow 0.0.0.0 IN or OUT traffic in Region $regx" "$regx" else - textOK "No Default Security Groups open to 0.0.0.0 found in Region $regx " + textOK "No Default Security Groups open to 0.0.0.0 found in Region $regx" "$regx" fi done } @@ -1279,7 +1296,7 @@ check45(){ for regx in $REGIONS; do LIST_OF_VPCS_PEERING_CONNECTIONS=$($AWSCLI ec2 describe-vpc-peering-connections --output text --profile $PROFILE --region $regx --query 'VpcPeeringConnections[*].VpcPeeringConnectionId') if [[ $LIST_OF_VPCS_PEERING_CONNECTIONS ]];then - textNotice "$regx: $LIST_OF_VPCS_PEERING_CONNECTIONS, review its routing tables " + textNotice "$regx: $LIST_OF_VPCS_PEERING_CONNECTIONS, review its routing tables" "$regx" #LIST_OF_VPCS=$($AWSCLI ec2 describe-vpcs --profile $PROFILE --region $regx --query 'Vpcs[*].VpcId' --output text) #aws ec2 describe-route-tables --filter "Name=vpc-id,Values=vpc-0213e864" --query "RouteTables[*].{RouteTableId:RouteTableId, VpcId:VpcId, Routes:Routes, AssociatedSubnets:Associations[*].SubnetId}" --profile $PROFILE --region $regx # for vpc in $LIST_OF_VPCS; do @@ -1287,7 +1304,7 @@ check45(){ # done #echo $VPCS_WITH_PEERING else - textOK "$regx: No VPC peering found " + textOK "$regx: No VPC peering found" "$regx" fi done } @@ -1453,5 +1470,7 @@ check43 check44 check45 -infoReferenceLong +if [[ $MODE != "csv" ]]; then + infoReferenceLong +fi cleanTemp From e66bb7f1abb8affec482e15d24f7ec836147d6bb Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 16:13:16 -0500 Subject: [PATCH 14/22] tiny output tweaks --- prowler | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/prowler b/prowler index 7ce453a1..a6474e52 100755 --- a/prowler +++ b/prowler @@ -305,7 +305,8 @@ textTitle(){ printCsvHeader() { >&2 echo "Generateing pipe delimited report on stdout; Diagnostics on stderr." - >&2 echo " " + >&2 echo " Using Profile $PROFILE" + >&2 echo "" echo "PROFILE|REGION|TITLE_ID|RESULT|SCORED|TITLE_TEXT|NOTES" } @@ -351,7 +352,7 @@ printColorsCode(){ # Generate Credential Report genCredReport() { - textTitle "0.0" "Generating AWS IAM Credential Report..." + textTitle "0.1" "Generating AWS IAM Credential Report..." until $( $AWSCLI iam generate-credential-report --output text --query 'State' --profile $PROFILE --region $REGION |grep -q -m 1 "COMPLETE") ; do sleep 1 done From 1eee23e4e1136ad29f1f0e0f820f6a2a3547d6c7 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 16:21:07 -0500 Subject: [PATCH 15/22] documentation & formatting updates * add M switch to help, readme * remove title_id data from mono / color output lines --- prowler | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prowler b/prowler index a6474e52..9a07af20 100755 --- a/prowler +++ b/prowler @@ -49,7 +49,7 @@ USAGE: -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) -b do not use colors in the output - -M output mode: text (defalut), mono, csv + -M output mode: text (defalut), mono, csv (separator is \"|\"; data is on stdout; progress on stderr) -h this help " exit @@ -252,7 +252,7 @@ textOK(){ fi echo "$PROFILE|$REPREGION|$TITLE_ID|PASS|$ITEM_SCORED|$TITLE_TEXT|$1" else - echo " $TITLE_ID $OK OK! $NORMAL $1" + echo " $OK OK! $NORMAL $1" fi } @@ -265,7 +265,7 @@ textNotice(){ fi echo "$PROFILE|$REPREGION|$TITLE_ID|INFO|$ITEM_SCORED|$TITLE_TEXT|$1" else - echo " $TITLE_ID $NOTICE INFO! $1 $NORMAL" + echo " $NOTICE INFO! $1 $NORMAL" fi } @@ -278,7 +278,7 @@ textWarn(){ fi echo "$PROFILE|$REPREGION|$TITLE_ID|WARNING|$ITEM_SCORED|$TITLE_TEXT|$1" else - echo " $TITLE_ID $BAD WARNING! $1 $NORMAL" + echo " $BAD WARNING! $1 $NORMAL" fi } From 8cba6df362d5e5a53f2c7c09650375528f1b12ce Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 16:34:02 -0500 Subject: [PATCH 16/22] add output mode to README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index c5209528..55572996 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,10 @@ or if you want a colored HTML report do: pip install ansi2html ./prowler | ansi2html -la > report.html ``` +or if you want a pipe-delimited report file, do: +``` +./prowler -M csv > output.psv +``` 5 - For help use: @@ -94,6 +98,7 @@ USAGE: -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) -b do not use colors in the output + -M output mode: text (defalut), mono, csv (separator is "|"; data is on stdout; progress on stderr) -h this help ``` From 85e620345c2381af977a2b8aee9bae280ee4aedf Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 16:35:56 -0500 Subject: [PATCH 17/22] remove -b flag, since -M covers it --- README.md | 1 - prowler | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 55572996..0fbda960 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,6 @@ USAGE: -c specify a check number or group from the AWS CIS benchmark (i.e.: check11 for check 1.1 or check3 for entire section 3) -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) - -b do not use colors in the output -M output mode: text (defalut), mono, csv (separator is "|"; data is on stdout; progress on stderr) -h this help diff --git a/prowler b/prowler index 9a07af20..987c3c49 100755 --- a/prowler +++ b/prowler @@ -48,22 +48,18 @@ USAGE: -c specify a check number or group from the AWS CIS benchmark (i.e.: check11 for check 1.1 or check3 for entire section 3) -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) - -b do not use colors in the output -M output mode: text (defalut), mono, csv (separator is \"|\"; data is on stdout; progress on stderr) -h this help " exit } -while getopts ":hbp:r:c:f:m:M:" OPTION; do +while getopts ":hp:r:c:f:m:M:" OPTION; do case $OPTION in h ) usage exit 1 ;; - b ) - MONOCHROME=1 - ;; p ) PROFILE=$OPTARG ;; @@ -304,6 +300,8 @@ textTitle(){ } printCsvHeader() { + >&2 echo "" + >&2 echo "" >&2 echo "Generateing pipe delimited report on stdout; Diagnostics on stderr." >&2 echo " Using Profile $PROFILE" >&2 echo "" From 43527302acdfdf92a617ef741f2e4fe03af42aa5 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 16:45:17 -0500 Subject: [PATCH 18/22] remove commas from output text --- prowler | 98 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/prowler b/prowler index 987c3c49..85112356 100755 --- a/prowler +++ b/prowler @@ -391,9 +391,9 @@ infoReferenceShort(){ check11(){ ID11="1.1" TITLE11="Avoid the use of the root account (Scored)." - COMMAND11=$(cat $TEMP_REPORT_FILE| grep '' | cut -d, -f5,11,16 | sed 's/,/,\ /g') + COMMAND11=$(cat $TEMP_REPORT_FILE| grep '' | cut -d, -f5,11,16 | sed 's/,/\ /g') textTitle "$ID11" "$TITLE11" - textNotice "Root account last accessed (password, key_1, key_2): $COMMAND11" + textNotice "Root account last accessed (password key_1 key_2): $COMMAND11" } check12(){ @@ -636,7 +636,7 @@ check115(){ # No command available textTitle "$ID115" "$TITLE115" "0" textNotice "No command available for check 1.15 " - textNotice "Login to the AWS Console as root, click on the Account " + textNotice "Login to the AWS Console as root & click on the Account " textNotice "Name -> My Account -> Configure Security Challenge Questions " } @@ -735,7 +735,7 @@ check122(){ textOK "Support Policy attached to $POLICYTOSHOW" textNotice "Make sure your team can create a Support case with AWS " else - textWarn "Support Policy not applied to any Group, User or Role " + textWarn "Support Policy not applied to any Group / User / Role " fi done else @@ -774,7 +774,7 @@ check124(){ textTitle "$ID124" "$TITLE124" LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text --profile $PROFILE --region $REGION|grep 'arn:aws:iam::[0-9]\{12\}:'|awk '{ print $2 }') if [[ $LIST_CUSTOM_POLICIES ]]; then - textNotice "Looking for custom policies: (skipping default policies, it may take few seconds...)" + 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 $PROFILE --region $REGION --query 'Policies[*].[Arn,DefaultVersionId]' --output text|grep -w $policy |awk '{ 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 $PROFILE --region $REGION) @@ -923,7 +923,7 @@ check27(){ if [[ $CLOUDTRAILENC_ENABLED ]];then textOK "KMS key found for $trail" else - textWarn "encryption is not enabled in your CloudTrail trail $trail, KMS key not found!" + textWarn "encryption is not enabled in your CloudTrail trail $trail but KMS key not found!" fi done else @@ -970,12 +970,12 @@ check31(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep AccessDenied) if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters for Access Denied enabled" + textOK "CloudWatch group found with metric filters for Access Denied enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -987,12 +987,12 @@ check32(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'userIdentity.sessionContext.attributes.mfaAuthenticated.*true') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters for sign-in Console without MFA enabled" + 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" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1004,12 +1004,12 @@ check33(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters for usage of root account enabled" + 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" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1021,12 +1021,12 @@ check34(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --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, and metric filters for IAM policy changes enabled" + textOK "CloudWatch group found with metric filters for IAM policy changes enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1038,12 +1038,12 @@ check35(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters for CloudTrail configuration changes enabled" + textOK "CloudWatch group found with metric filters for CloudTrail configuration changes enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1055,12 +1055,12 @@ check36(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters for usage of root account enabled" + 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" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1072,12 +1072,12 @@ check37(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found with metric filters enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1089,12 +1089,12 @@ check38(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --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, and metric filters enabled" + textOK "CloudWatch group found with metric filters enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1106,12 +1106,12 @@ check39(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found with metric filters enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1123,12 +1123,12 @@ check310(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found with metric filters enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1140,12 +1140,12 @@ check311(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found with metric filters enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1157,12 +1157,12 @@ check312(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found with metric filters enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1174,12 +1174,12 @@ check313(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --region $REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable') if [[ $METRICFILTER_SET ]];then - textOK "CloudWatch group found, and metric filters enabled" + textOK "CloudWatch group found with metric filters enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1191,12 +1191,12 @@ check314(){ if [[ $CLOUDWATCH_GROUP ]];then METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $CLOUDWATCH_GROUP --profile $PROFILE --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, and metric filters enabled" + textOK "CloudWatch group found with metric filters enabled" else - textWarn "CloudWatch group found, but no metric filters or alarms associated" + textWarn "CloudWatch group found but no metric filters or alarms associated" fi else - textWarn "No CloudWatch group found, no metric filters or alarms associated" + textWarn "No CloudWatch group found but no metric filters or alarms associated" fi } @@ -1295,7 +1295,7 @@ check45(){ for regx in $REGIONS; do LIST_OF_VPCS_PEERING_CONNECTIONS=$($AWSCLI ec2 describe-vpc-peering-connections --output text --profile $PROFILE --region $regx --query 'VpcPeeringConnections[*].VpcPeeringConnectionId') if [[ $LIST_OF_VPCS_PEERING_CONNECTIONS ]];then - textNotice "$regx: $LIST_OF_VPCS_PEERING_CONNECTIONS, review its routing tables" "$regx" + textNotice "$regx: $LIST_OF_VPCS_PEERING_CONNECTIONS - review routing tables" "$regx" #LIST_OF_VPCS=$($AWSCLI ec2 describe-vpcs --profile $PROFILE --region $regx --query 'Vpcs[*].VpcId' --output text) #aws ec2 describe-route-tables --filter "Name=vpc-id,Values=vpc-0213e864" --query "RouteTables[*].{RouteTableId:RouteTableId, VpcId:VpcId, Routes:Routes, AssociatedSubnets:Associations[*].SubnetId}" --profile $PROFILE --region $regx # for vpc in $LIST_OF_VPCS; do From e9122b3453f659507119a7b312028673ea252f54 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 16:50:57 -0500 Subject: [PATCH 19/22] move CSV separator character to a variable --- prowler | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/prowler b/prowler index 85112356..bddd0e0f 100755 --- a/prowler +++ b/prowler @@ -36,6 +36,8 @@ FILTERREGION="" MAXITEMS=100 MONOCHROME=0 MODE="text" +SEP=',' + # Command usage menu usage(){ @@ -48,7 +50,7 @@ USAGE: -c specify a check number or group from the AWS CIS benchmark (i.e.: check11 for check 1.1 or check3 for entire section 3) -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) + -M output mode: text (defalut), mono, csv (separator is \"${SEP}\"; data is on stdout; progress on stderr) -h this help " exit @@ -246,7 +248,7 @@ textOK(){ else REPREGION=$REGION fi - echo "$PROFILE|$REPREGION|$TITLE_ID|PASS|$ITEM_SCORED|$TITLE_TEXT|$1" + echo "$PROFILE${SEP}$REPREGION${SEP}$TITLE_ID${SEP}PASS${SEP}$ITEM_SCORED${SEP}$TITLE_TEXT${SEP}$1" else echo " $OK OK! $NORMAL $1" fi @@ -259,7 +261,7 @@ textNotice(){ else REPREGION=$REGION fi - echo "$PROFILE|$REPREGION|$TITLE_ID|INFO|$ITEM_SCORED|$TITLE_TEXT|$1" + echo "$PROFILE${SEP}$REPREGION${SEP}$TITLE_ID${SEP}INFO${SEP}$ITEM_SCORED${SEP}$TITLE_TEXT${SEP}$1" else echo " $NOTICE INFO! $1 $NORMAL" fi @@ -272,7 +274,7 @@ textWarn(){ else REPREGION=$REGION fi - echo "$PROFILE|$REPREGION|$TITLE_ID|WARNING|$ITEM_SCORED|$TITLE_TEXT|$1" + echo "$PROFILE${SEP}$REPREGION${SEP}$TITLE_ID${SEP}WARNING${SEP}$ITEM_SCORED${SEP}$TITLE_TEXT${SEP}$1" else echo " $BAD WARNING! $1 $NORMAL" fi @@ -302,10 +304,10 @@ textTitle(){ printCsvHeader() { >&2 echo "" >&2 echo "" - >&2 echo "Generateing pipe delimited report on stdout; Diagnostics on stderr." + >&2 echo "Generateing \"${SEP}\" delimited report on stdout; Diagnostics on stderr." >&2 echo " Using Profile $PROFILE" >&2 echo "" - echo "PROFILE|REGION|TITLE_ID|RESULT|SCORED|TITLE_TEXT|NOTES" + echo "PROFILE${SEP}REGION${SEP}TITLE_ID${SEP}RESULT${SEP}SCORED${SEP}TITLE_TEXT${SEP}NOTES" } prowlerBanner() { From 8232a07437d61962f2d7a087a86d9c45f67c5167 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Mon, 26 Jun 2017 17:06:17 -0500 Subject: [PATCH 20/22] clean up usage of expr --- prowler | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/prowler b/prowler index bddd0e0f..1ed69d12 100755 --- a/prowler +++ b/prowler @@ -455,7 +455,6 @@ check14(){ textTitle "$ID14" "$TITLE14" C14_NUM_USERS1=0 C14_NUM_USERS2=0 - # $(expr $C116_NUM_USERS + 1) if [[ $LIST_OF_USERS_WITH_ACCESS_KEY1 ]]; then # textWarn "Users with access key 1 older than 90 days:" for user in $LIST_OF_USERS_WITH_ACCESS_KEY1; do @@ -465,7 +464,7 @@ check14(){ if [ $HOWOLDER -gt "90" ];then textWarn " $user has not rotated access key1 in over 90 days " - C14_NUM_USERS1 = $(expr $C14_NUM_USERS1 + 1) + C14_NUM_USERS1=$(expr $C14_NUM_USERS1 + 1) fi done if [[ $C14_NUM_USERS1 -eq 0 ]]; then @@ -483,7 +482,7 @@ check14(){ HOWOLDER=$(how_older_from_today $DATEROTATED2) if [ $HOWOLDER -gt "90" ];then textWarn " $user has not rotated access key2. " - C14_NUM_USERS2 = $(expr $C14_NUM_USERS2 + 1) + C14_NUM_USERS2=$(expr $C14_NUM_USERS2 + 1) fi done if [[ $C14_NUM_USERS2 -eq 0 ]]; then From 085ddae3b988de5bae20e6e3c6aac15246b1aec1 Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Tue, 27 Jun 2017 08:52:55 -0500 Subject: [PATCH 21/22] capture script start time using well supported date arguments --- prowler | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prowler b/prowler index 1ed69d12..d23f90dd 100755 --- a/prowler +++ b/prowler @@ -147,6 +147,8 @@ else WHITE="" 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 # function to compare in days, usage how_older_from_today date @@ -324,7 +326,7 @@ getWhoami(){ if [[ $MODE == "csv" ]]; then CALLER_ARN=$($AWSCLI sts get-caller-identity --output json --profile $PROFILE --region $REGION --query "Arn" | tr -d '"') textTitle "0.0" "Show report generation info" - textNotice "ARN: $CALLER_ARN TIMESTAMP: $(date -Is)" + textNotice "ARN: $CALLER_ARN TIMESTAMP: $SCRIPT_START_TIME" else echo "" echo "This report is being generated using credentials below:" From 94efa3028a78dfd8e7e97ea8b4e9ed721dabc63f Mon Sep 17 00:00:00 2001 From: Ben Allen Date: Tue, 27 Jun 2017 09:06:29 -0500 Subject: [PATCH 22/22] clean up CSV output for 1.23 when multiple users match --- prowler | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/prowler b/prowler index d23f90dd..0ad7f0fb 100755 --- a/prowler +++ b/prowler @@ -755,8 +755,9 @@ check123(){ 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 - # textNotice "List of users with Access Key 1 never used:" - textNotice "Users who never used Access Key 1: $LIST_USERS_KEY1_ACTIVE" + for user in $LIST_USERS_KEY1_ACTIVE; do + textNotice "$user has never used Access Key 1" + done else textOK "No users found with Access Key 1 never used" fi @@ -764,8 +765,9 @@ check123(){ 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 - # textNotice "List of users with Access Key 2 never used:" - textNotice "Users who never used Access Key 2: $LIST_USERS_KEY2_ACTIVE" + for user in $LIST_USERS_KEY2_ACTIVE; do + textNotice "$user has never used Access Key 2" + done else textOK "No users found with Access Key 2 never used" fi