Files
prowler/prowler.sh
2016-09-13 00:01:11 -04:00

812 lines
36 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Prowler is a tool that provides automate auditing and hardening guidance of an AWS account.
# It is based on AWS-CLI commands. It follows guidelines present in the CIS Amazon
# Web Services Foundations Benchmark at:
# https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
# This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0
# International Public License. The link to the license terms can be found at
# https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
#
# Author: Toni de la Fuente - @ToniBlyx / Alfresco Software Inc.
# Prowler - Iron Maiden
#
# Walking through the city, looking oh so pretty
# I've just got to find my way
# See the ladies flashing
# All there legs and lashes
# I've just got to find my way...
# Safety feature: exit script if error is returned, or if variables not set.
# Exit if a pipeline results in an error.
# set -ue
# set -o pipefail
# set -vx
# Exits if any error is found
#set -e
# Colors
NORMAL=""
WARNING="" # Bad (red)
SECTION="" # Section (yellow)
NOTICE="" # Notice (yellow)
OK="" # Ok (green)
BAD="" # Bad (red)
CYAN=""
BLUE=""
BROWN=""
DARKGRAY=""
GRAY=""
GREEN=""
MAGENTA=""
PURPLE=""
RED=""
YELLOW=""
WHITE=""
DEFULT_AWS_PROFILE="default"
DEFAULT_AWS_REGION="us-east-1"
# Command usage menu
usage(){
echo -e "\nUSAGE:
`basename $0` -p <profile> -r <region> [ -v ] [ -h ]
Options:
-p <profile> specify your AWS profile to use (i.e.: default)
-r <region> specify a desired AWS region to use (i.e.: us-east-1)
-v enable vervose mode
-h this help
"
exit
}
while getopts "hp:r:" OPTION; do
case $OPTION in
h )
usage
exit 1
;;
p )
PROFILE=$OPTARG
;;
r )
REGION=$OPTARG
;;
: )
echo -e "\n$RED ERROR!$NORMAL -$OPTARG requires an argument\n"
exit 1
;;
? )
echo -e "\n$RED ERROR!$NORMAL Invalid option"
usage
exit 1
;;
esac
done
# Functions to manage dates depending on OS
if [[ "$OSTYPE" == "linux-gnu" ]]; then
# function to compare in days, usage how_older_from_today date
# date format %Y-%m-%d
how_older_from_today()
{
DATE_TO_COMPARE=$1
TODAY_IN_DAYS=$(date -d "$(date +%Y-%m-%d)" +%s)
DATE_FROM_IN_DAYS=$(date -d $DATE_TO_COMPARE +%s)
DAYS_SINCE=$((($TODAY_IN_DAYS - $DATE_FROM_IN_DAYS )/60/60/24))
echo $DAYS_SINCE
}
# function to convert from timestamp to date, usage timestamp_to_date timestamp
# output date format %Y-%m-%d
timestamp_to_date()
{
# remove fractions of a second
TIMESTAMP_TO_CONVERT=$(echo $1 | cut -f1 -d".")
OUTPUT_DATE=$(date -d @$TIMESTAMP_TO_CONVERT +'%Y-%m-%d')
echo $OUTPUT_DATE
}
elif [[ "$OSTYPE" == "darwin"* ]]; then
# BSD/OSX coommands compatibility
how_older_from_today()
{
DATE_TO_COMPARE=$1
TODAY_IN_DAYS=$(date +%s)
DATE_FROM_IN_DAYS=$(date -jf %Y-%m-%d $DATE_TO_COMPARE +%s)
DAYS_SINCE=$((($TODAY_IN_DAYS - $DATE_FROM_IN_DAYS )/60/60/24))
echo $DAYS_SINCE
}
timestamp_to_date()
{
# remove fractions of a second
TIMESTAMP_TO_CONVERT=$(echo $1 | cut -f1 -d".")
OUTPUT_DATE=$(date -r $TIMESTAMP_TO_CONVERT +'%Y-%m-%d')
echo $OUTPUT_DATE
}
elif [[ "$OSTYPE" == "cygwin" ]]; then
# POSIX compatibility layer and Linux environment emulation for Windows
how_older_from_today()
{
DATE_TO_COMPARE=$1
TODAY_IN_DAYS=$(date -d "$(date +%Y-%m-%d)" +%s)
DATE_FROM_IN_DAYS=$(date -d $DATE_TO_COMPARE +%s)
DAYS_SINCE=$((($TODAY_IN_DAYS - $DATE_FROM_IN_DAYS )/60/60/24))
echo $DAYS_SINCE
}
timestamp_to_date()
{
# remove fractions of a second
TIMESTAMP_TO_CONVERT=$(echo $1 | cut -f1 -d".")
OUTPUT_DATE=$(date -d @$TIMESTAMP_TO_CONVERT +'%Y-%m-%d')
echo $OUTPUT_DATE
}
else
echo "Unknown Operating System"
exit
fi
if (($# == 0)); then
PROFILE=$DEFULT_AWS_PROFILE
REGION=$DEFAULT_AWS_REGION
fi
if [[ ! -f ~/.aws/credentials ]]; then
echo -e "\n$RED ERROR!$NORMAL AWS credentials file not found (~/.aws/credentials). Run 'aws configure' first. \n"
return 1
fi
# AWS-CLI variables
AWSCLI=$(which aws)
if [ -z "${AWSCLI}" ]; then
echo -e "\n$RED ERROR!$NORMAL AWS-CLI (aws command) not found. Make sure it is installed correctly and in your \$PATH\n"
exit
fi
# if [ -z "${PROFILE}" ] || [ -z "${REGION}" ]; then
# PROFILE=$($AWSCLI configure list | grep "profile" | awk '{ print $2 }')
# REGION=$($AWSCLI configure list | grep "region" | awk '{ print $2 }')
# if [ -z "${PROFILE}" ] || [ -z "${REGION}" ]; then
# echo -e "\n $RED ERROR!$NORMAL No profile or region found, configure it using 'aws configure'\n"
# echo -e " or specify options -p <profile> -r <region>\n"
# exit
# fi
# fi
# if this script runs in an AWS instance
# INSTANCE_PROFILE=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/)
# AWS_ACCESS_KEY_ID=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep AccessKeyId | cut -d':' -f2 | sed 's/[^0-9A-Z]*//g')
# AWS_SECRET_ACCESS_KEY_ID=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/${INSTANCE_PROFILE} | grep SecretAccessKey | cut -d':' -f2 | sed 's/[^0-9A-Za-z/+=]*//g')
# AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
# AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY_ID}
#cat ~/.aws/credentials
prowlerBanner() {
echo -e "$CYAN _"
echo -e " _ __ _ __ _____ _| | ___ _ __"
echo -e " | '_ \| '__/ _ \ \ /\ / / |/ _ \ '__|"
echo -e " | |_) | | | (_) \ V V /| | __/ |"
echo -e " | .__/|_| \___/ \_/\_/ |_|\___|_|"
echo -e " |_|$NORMAL$BLUE CIS based AWS Account Hardening Tool$NORMAL\n"
}
# Get whoami in AWS, who is the user running this shell script
getWhoami() {
echo -e "\nThis report is being generated using credentials below:\n"
echo -e "AWS-CLI Profile: $NOTICE[$PROFILE]$NORMAL AWS Region: $NOTICE[$REGION]$NORMAL\n"
$AWSCLI sts get-caller-identity --output table --profile $PROFILE --region $REGION
}
prowlerBanner
echo -e "\nDate: $NOTICE$(date)$NORMAL"
getWhoami
echo -e "\nColors Code for results: $NOTICE INFORMATIVE$NORMAL,$OK OK (RECOMMENDED VALUE)$NORMAL, $BAD CRITICAL (FIX REQUIRED)$NORMAL \n"
# Generate Credential Report
genCredReport() {
echo -en '\nGenerating Credential Report...'
while STATE=$($AWSCLI iam generate-credential-report --output text --query 'State' --profile $PROFILE --region $REGION)
test "$STATE" = "STARTED"
do
sleep 1
echo -n '.'
done
echo -en " COMPLETE!"
}
genCredReport
# Save report to a file, deletion at finish. ACB stands for AWS CIS Benchmark
TEMP_REPORT_FILE=/tmp/.acb
$AWSCLI iam get-credential-report --query 'Content' --output text --profile $PROFILE --region $REGION | base64 -D > $TEMP_REPORT_FILE
# Get a list of all available AWS Regions
REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' \
--output text \
--profile $PROFILE \
--region $REGION)
TITLE1="$BLUE 1 Identity and Access Management *********************************$NORMAL"
echo -e "\n\n$TITLE1 "
# 1.1
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 '<root_account>' | cut -d, -f5,11,16)
echo -e "\n$TITLE11 $NOTICE $COMMAND11 $NORMAL"
# 1.2
TITLE12="$BLUE 1.2$NORMAL 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 $i| grep false | awk '{ print $1 }'|tr '\n' ' ';
done)
echo -e "\n$TITLE12"
if [[ $COMMAND12 ]]; then
echo " List of users with Password enabled but MFA disabled: $RED $COMMAND12 $NORMAL"
else
echo " $OK CORRECT! No users found with Password enabled and MFA disabled $NORMAL"
fi
# 1.3
TITLE13="$BLUE 1.3$NORMAL Ensure credentials unused for 90 days or greater are disabled (Scored)"
COMMAND13=$(
for i in $COMMAND12_LIST_USERS_WITH_PASSWORD_ENABLED; do
cat $TEMP_REPORT_FILE|awk -F, '{ print $1,$5 }' |grep $i| grep false | awk '{ print $1 }'|tr '\n' ' ';
done)
# list of users that have used password
USERS_PASSWORD_USED=$($AWSCLI iam list-users --query "Users[?PasswordLastUsed].UserName" --output text --profile $PROFILE --region $REGION)
echo -e "\n$TITLE13 "
# look for users with a password last used more or equal to 90 days
echo -e " User list: "
for i in $USERS_PASSWORD_USED; do
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 " $RED $i $NORMAL"
else
echo " $OK OK $NORMAL"
fi
done
# 1.4
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 "
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)
if [ $HOWOLDER -gt "90" ];then
echo -e " $RED $user $NORMAL"
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
echo -e " $RED $user $NORMAL"
fi
done
# 1.5
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 "
if [ $COMMAND15 == "True" ];then
echo -e " $OK OK $NORMAL"
else
echo -e " $RED FALSE $NORMAL"
fi
# 1.6
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 "
if [ $COMMAND16 == "True" ];then
echo -e " $OK OK $NORMAL"
else
echo -e " $RED FALSE $NORMAL"
fi
# 1.7
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 "
if [ $COMMAND17 == "True" ];then
echo -e " $OK OK $NORMAL"
else
echo -e " $RED FALSE $NORMAL"
fi
# 1.8
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 "
if [ $COMMAND18 == "True" ];then
echo -e " $OK OK $NORMAL"
else
echo -e " $RED FALSE $NORMAL"
fi
# 1.9
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 "
if [ $COMMAND19 -gt "13" ];then
echo -e " $OK OK $NORMAL"
else
echo -e " $RED FALSE $NORMAL"
fi
# 1.10
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' | grep PasswordReusePrevention | awk -F: '{ print $2 }'|sed 's/\ //g'|sed 's/,/ /g')
echo -e "\n$TITLE110 "
if [[ $COMMAND110 ]];then
if [[ $COMMAND110 -gt "23" ]];then
echo -e " $OK OK $NORMAL"
fi
else
echo -e " $RED FALSE $NORMAL"
fi
# 1.11
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 "
if [[ $COMMAND111 ]];then
if [ $COMMAND111 == "90" ];then
echo -e " $OK OK $NORMAL"
fi
else
echo -e " $RED FALSE $NORMAL"
fi
# 1.12
TITLE112="$BLUE 1.12$NORMAL 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 }')
echo -e "\n$TITLE112 "
if [ $ROOTKEY1 == false ];then
echo -e " $OK OK $NORMAL No access key 1 found "
else
echo -e " $RED Found access key 1 $NORMAL"
fi
if [ $ROOTKEY2 == false ];then
echo -e " $OK OK $NORMAL No access key 2 found "
else
echo -e " $RED Found access key 2 $NORMAL"
fi
# 1.13
TITLE113="$BLUE 1.13$NORMAL Ensure hardware MFA is enabled for the root account (Scored)"
COMMAND113=$($AWSCLI iam list-virtual-mfa-devices --profile $PROFILE --region $REGION --query 'VirtualMFADevices[*].User.Arn' --output text | awk -F":" '{ print $6 }'|tr '\n' ' ')
echo -e "\n$TITLE113"
if [ $COMMAND113 ]; then
echo " $OK OK $NORMAL"
else
echo " $RED WARNING, MFA is not ENABLED for root account $NORMAL"
fi
# 1.14
TITLE114="$BLUE 1.14$NORMAL Ensure security questions are registered in the AWS account (Not Scored)"
# No command available
echo -e "\n$TITLE114"
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"
# 1.15
TITLE115="$BLUE 1.15$NORMAL Ensure IAM policies are attached only to groups or roles (Scored)"
echo -e "\n$TITLE115"
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...) "
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 " $RED $user $NORMAL"
fi
done
TITLE2="$BLUE 2 Logging ********************************************************$NORMAL"
echo -e "\n\n$TITLE2 "
TITLE21="$BLUE 2.1$NORMAL Ensure CloudTrail is enabled in all regions (Scored)"
echo -e "\n$TITLE21"
COMMAND21=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].IsMultiRegionTrail' --output text)
if [[ $COMMAND21 ]];then
if [ $COMMAND21 == "True" ];then
echo -e " $OK OK $NORMAL"
fi
else
echo -e " $RED FALSE $NORMAL"
fi
TITLE22="$BLUE 2.2$NORMAL Ensure CloudTrail log file validation is enabled (Scored)"
echo -e "\n$TITLE22"
COMMAND22=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --query 'trailList[*].LogFileValidationEnabled' --output text
)
if [[ $COMMAND22 ]];then
if [ $COMMAND22 == "True" ];then
echo -e " $OK OK $NORMAL"
fi
else
echo -e " $RED FALSE $NORMAL"
fi
TITLE23="$BLUE 2.3$NORMAL Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)"
echo -e "\n$TITLE23"
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION)
if [[ $CLOUDTRAILBUCKET ]];then
CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $CLOUDTRAILBUCKET --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --profile $PROFILE --region $REGION --output text)
# aws s3api get-bucket-policy --bucket $CLOUDTRAILBUCKET --profile $PROFILE --region $REGION --output text
if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]];then
echo -e " $RED WARNING, check your CloudTrail bucket ACL and Policy!$NORMAL"
else
echo -e " $OK OK $NORMAL"
fi
else
echo -e " $RED WARNING, CloudTrail bucket doesn't exist!$NORMAL"
fi
TITLE24="$BLUE 2.4$NORMAL Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)"
echo -e "\n$TITLE24"
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
LATESTDELIVERY_TIMESTAMP=$($AWSCLI cloudtrail get-trail-status --name $trail --profile $PROFILE --region $REGION --query 'LatestCloudWatchLogsDeliveryTime')
LATESTDELIVERY_DATE=$(timestamp_to_date $LATESTDELIVERY_TIMESTAMP)
HOWOLDER=$(how_older_from_today $LATESTDELIVERY_DATE)
if [ $HOWOLDER -gt "1" ];then
echo -e " $RED $trail is not logging in the last 24h $NORMAL"
else
echo -e " $OK $trail has been logging during the last 24h $NORMAL"
fi
done
else
echo -e " $RED WARNING, No CloudTrail trails found!$NORMAL"
fi
TITLE25="$BLUE 2.5$NORMAL Ensure AWS Config is enabled in all regions (Scored)"
echo -e "\n$TITLE25"
for regx in $REGIONS; do
CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status --profile $PROFILE --region $regx | grep recorder)
if [[ $CHECK_AWSCONFIG_STATUS ]];then
echo -e " $OK Region $regx has AWS Config $CHECK_AWSCONFIG_STATUS $NORMAL"
else
echo -e " $RED WARNING, Region $regx has AWS Config disabled or not configured$NORMAL"
fi
done
TITLE26="$BLUE 2.6$NORMAL Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)"
echo -e "\n$TITLE26"
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text --profile $PROFILE --region $REGION)
if [[ $CLOUDTRAILBUCKET ]];then
CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $CLOUDTRAILBUCKET --profile $PROFILE --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None)
if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then
echo -e " $OK OK $NORMAL"
else
echo -e " $RED WARNING, access logging is not enabled in your CloudTrail S3 bucket!$NORMAL"
fi
else
echo -e " $RED WARNING, CloudTrail bucket doesn't exist!$NORMAL"
fi
TITLE27="$BLUE 2.7$NORMAL Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)"
echo -e "\n$TITLE27"
CLOUDTRAILNAME=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].Name' --output text --profile $PROFILE --region $REGION)
if [[ $CLOUDTRAILNAME ]];then
CLOUDTRAILENC_ENABLED=$($AWSCLI cloudtrail describe-trails --profile $PROFILE --region $REGION --trail $CLOUDTRAILNAME --query 'trailList[*].KmsKeyId' --output text)
if [[ $CLOUDTRAILENC_ENABLED ]];then
echo -e " $OK OK $NORMAL"
else
echo -e " $RED WARNING, encryption is not enabled in your CloudTrail trail, KMS key not found!$NORMAL"
fi
else
echo -e " $RED WARNING, CloudTrail bucket doesn't exist!$NORMAL"
fi
TITLE28="$BLUE 2.8$NORMAL Ensure rotation for customer created CMKs is enabled (Scored)"
echo -e "\n$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
for key in $CHECK_KMS_KEYLIST; do
CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key --profile $PROFILE --region $regx --output text)
if [ $CHECK_KMS_KEY_ROTATION == "True" ];then
echo -e " $OK OK $NORMAL, Key $key in Region $regx is set correctly"
else
echo -e " $RED WARNING, Key $key in Region $regx is not set to rotate!$NORMAL"
fi
done
else
echo -e " $NOTICE Region $regx doesn't have encryption keys $NORMAL"
fi
done
TITLE3="$BLUE 3 Monitoring *****************************************************"
echo -e "\n\n$TITLE3 "
# 3 Monitoring check commands / Mostly covered by SecurityMonkey
TITLE31="$BLUE 3.1$NORMAL Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)"
echo -e "\n$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 CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep AccessDenied)
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters for Access Denied enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
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 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'userIdentity.sessionContext.attributes.mfaAuthenticated.*true')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters for sign-in Console without MFA enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE33="$BLUE 3.3$NORMAL Ensure a log metric filter and alarm exist for usage of root account (Scored)"
echo -e "\n$TITLE33 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'Root.*AwsServiceEvent')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters for usage of root account enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE34="$BLUE 3.4$NORMAL Ensure a log metric filter and alarm exist for IAM policy changes (Scored)"
echo -e "\n$TITLE34 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | 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, CloudWatch group found, and metric filters for IAM policy changes enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE35="$BLUE 3.5$NORMAL Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)"
echo -e "\n$TITLE35 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters for CloudTrail configuration changes enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE36="$BLUE 3.6$NORMAL Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)"
echo -e "\n$TITLE36 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'ConsoleLogin.*Failed')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters for usage of root account enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
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 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE38="$BLUE 3.8$NORMAL Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)"
echo -e "\n$TITLE38 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE39="$BLUE 3.9$NORMAL Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)"
echo -e "\n$TITLE39 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE310="$BLUE 3.10$NORMAL Ensure a log metric filter and alarm exist for security group changes (Scored)"
echo -e "\n$TITLE310 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
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 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE312="$BLUE 3.12$NORMAL Ensure a log metric filter and alarm exist for changes to network gateways (Scored)"
echo -e "\n$TITLE312 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE313="$BLUE 3.13$NORMAL Ensure a log metric filter and alarm exist for route table changes (Scored)"
echo -e "\n$TITLE313 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE314="$BLUE 3.14$NORMAL Ensure a log metric filter and alarm exist for VPC changes (Scored)"
echo -e "\n$TITLE314 "
if [[ $CLOUDWATCH_GROUP ]];then
METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name CloudTrail/CloudWatchGroup --profile $PROFILE --region $REGION --query 'trailList' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink')
if [[ $METRICFILTER_SET ]];then
echo -e " $OK OK, CloudWatch group found, and metric filters enabled$NORMAL"
else
echo -e " $RED WARNING, CloudWatch group found, but no metric filters or alarms associated$NORMAL"
fi
else
echo -e " $RED WARNING, No CloudWatch group found, no metric filters or alarms associated$NORMAL"
fi
TITLE315="$BLUE 3.15$NORMAL Ensure security contact information is registered (Scored)"
# No command available
echo -e "\n$TITLE315 "
echo -e " $NOTICE No command available for check 3.15"
echo -e " Login to the AWS Console, click on My Account "
echo -e " Go to Alternate Contacts -> make sure Security section is filled $NORMAL"
TITLE316="$BLUE 3.16$NORMAL Ensure appropriate subscribers to each SNS topic (Not Scored)"
echo -e "\n$TITLE316 "
for regx in $REGIONS; do
TOPICS_LIST=$($AWSCLI sns list-topics --profile $PROFILE --region $regx --output text --query 'Topics[*].TopicArn')
if [[ $TOPICS_LIST ]];then
for topic in $TOPICS_LIST; do
CHECK_TOPIC_LIST=$($AWSCLI sns list-subscriptions-by-topic --topic-arn $topic --profile $PROFILE --region $regx --query 'Subscriptions[*].{Endpoint:Endpoint,Protocol:Protocol}' --output text)
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"
else
echo -e " $RED WARNING, No suscription found in Region $regx and Topic $topic $NORMAL"
fi
done
else
echo -e " $NOTICE Region $regx doesn't have topics $NORMAL"
fi
done
TITLE4="$BLUE 4 Networking **************************************************$NORMAL"
echo -e "\n\n$TITLE4 "
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 "
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
echo -e " $RED Found Security Group: $SG open to 0.0.0.0/0 in Region $regx $NORMAL "
done
else
echo -e " $OK OK, No Security Groups found in $regx with port 22 TCP open to 0.0.0.0/0 $NORMAL "
fi
done
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 "
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
echo -e " $RED Found Security Group: $SG open to 0.0.0.0/0 in Region $regx $NORMAL "
done
else
echo -e " $OK OK, No Security Groups found in $regx with port 3389 TCP open to 0.0.0.0/0 $NORMAL "
fi
done
TITLE43="$BLUE 4.3$NORMAL Ensure VPC Flow Logging is Enabled in all Applicable Regions (Scored)"
echo -e "\n$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
echo -e " $OK OK, VPCFlowLog is enabled for LogGroupName: $FL in Region $regx $NORMAL "
done
else
echo -e " $RED WARNING, no VPCFlowLog has been found in Region $regx $NORMAL "
fi
done
TITLE44="$BLUE 4.4$NORMAL Ensure the default security group restricts all traffic (Scored)"
echo -e "\n$TITLE44 "
#COMMAND44= Ensure the default security group restricts all traffic
#aws ec2 describe-security-groups --filters Name=group-name,Values='default' --profile internalmg --region us-east-1
# Final
echo -e "\n$BLUE - For more information and reference:$NORMAL"
echo -e " $NOTICE https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf$NORMAL"
# Delete temp file
rm -fr $TEMP_REPORT_FILE