diff --git a/checks/check21 b/checks/check21 index d011cc1e..6dd8e214 100644 --- a/checks/check21 +++ b/checks/check21 @@ -23,7 +23,11 @@ check21(){ trail_count=0 # "Ensure CloudTrail is enabled in all regions (Scored)" for regx in $REGIONS; do - TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text | tr " " ',') + TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',') + if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then + textFail "Access Denied trying to describe trails in $regx" + continue + fi if [[ $TRAILS_AND_REGIONS ]]; then for reg_trail in $TRAILS_AND_REGIONS; do TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1) diff --git a/checks/check22 b/checks/check22 index 27250905..faf624a5 100644 --- a/checks/check22 +++ b/checks/check22 @@ -23,7 +23,11 @@ check22(){ trail_count=0 # "Ensure CloudTrail log file validation is enabled (Scored)" for regx in $REGIONS; do - TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text | tr " " ',') + TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',') + if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then + textFail "Access Denied trying to describe trails in $regx" + continue + fi if [[ $TRAILS_AND_REGIONS ]]; then for reg_trail in $TRAILS_AND_REGIONS; do TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1) @@ -35,9 +39,9 @@ check22(){ LOGFILEVALIDATION_TRAIL_STATUS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].LogFileValidationEnabled' --output text --trail-name-list $trail) if [[ "$LOGFILEVALIDATION_TRAIL_STATUS" == 'False' ]];then - textFail "Trail $trail in $regx has not log file validation enabled" + textFail "Trail $trail in $regx log file validation disabled" else - textPass "Trail $trail in $regx has log file validation enabled" + textPass "Trail $trail in $regx log file validation enabled" fi done diff --git a/checks/check23 b/checks/check23 index 237fdf68..149b7149 100644 --- a/checks/check23 +++ b/checks/check23 @@ -20,22 +20,68 @@ CHECK_ASFF_COMPLIANCE_TYPE_check23="ens-op.exp.10.aws.trail.3 ens-op.exp.10.aws. CHECK_SERVICENAME_check23="cloudtrail" check23(){ + trail_count=0 # "Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)" - CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text $PROFILE_OPT --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_OPT --region $REGION --output text 2>&1) - if [[ $(echo "$CLOUDTRAILBUCKET_HASALLPERMISIONS" | grep AccessDenied) ]]; then - textInfo "Access Denied Trying to Get Bucket Acl for $bucket" - continue - fi - if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]]; then - textFail "check your $bucket CloudTrail bucket ACL and Policy!" - else - textPass "Bucket $bucket is set correctly" - fi - done - else - textFail "No CloudTrail bucket found!" + for regx in $REGIONS; do + TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',') + if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then + textFail "Access Denied trying to describe trails in $regx" + continue + fi + if [[ $TRAILS_AND_REGIONS ]]; then + for reg_trail in $TRAILS_AND_REGIONS; do + TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1) + if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region + continue + fi + trail=$(echo $reg_trail | cut -d',' -f2) + trail_count=$((trail_count + 1)) + + CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].[S3BucketName]' --output text --trail-name-list $trail) + if [[ -z $CLOUDTRAILBUCKET ]]; then + textFail "Trail $trail in $TRAIL_REGION does not publish to S3" + continue + fi + + CLOUDTRAIL_ACCOUNT_ID=$(echo $trail | awk -F: '{ print $5 }') + if [ "$CLOUDTRAIL_ACCOUNT_ID" != "$ACCOUNT_NUM" ]; then + textInfo "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is not in current account" + continue + fi + + # + # LOCATION - requests referencing buckets created after March 20, 2019 + # must be made to S3 endpoints in the same region as the bucket was + # created. + # + BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $regx --bucket $CLOUDTRAILBUCKET --output text 2>&1) + if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then + textFail "Trail $trail in $TRAIL_REGION Access Denied getting bucket location for $CLOUDTRAILBUCKET" + continue + fi + if [[ $BUCKET_LOCATION == "None" ]]; then + BUCKET_LOCATION="us-east-1" + fi + if [[ $BUCKET_LOCATION == "EU" ]]; then + BUCKET_LOCATION="eu-west-1" + fi + + CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $CLOUDTRAILBUCKET $PROFILE_OPT --region $BUCKET_LOCATION --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --output text 2>&1) + if [[ $(echo "$CLOUDTRAILBUCKET_HASALLPERMISIONS" | grep AccessDenied) ]]; then + textInfo "Trail $trail in $TRAIL_REGION Access Denied getting bucket acl for $CLOUDTRAILBUCKET" + continue + fi + + if [[ -z $CLOUDTRAILBUCKET_HASALLPERMISIONS ]]; then + textPass "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is not publicly accessible" + else + textFail "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is publicly accessible" + fi + + done + fi + done + if [[ $trail_count == 0 ]]; then + textFail "No CloudTrail trails were found in the account" fi } diff --git a/checks/check24 b/checks/check24 index 0e018afd..e4265424 100644 --- a/checks/check24 +++ b/checks/check24 @@ -23,7 +23,11 @@ check24(){ trail_count=0 # "Ensure CloudTrail trails are integrated with CloudWatch Logs (Scored)" for regx in $REGIONS; do - TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text | tr " " ',') + TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',') + if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then + textFail "Access Denied trying to describe trails in $regx" + continue + fi if [[ $TRAILS_AND_REGIONS ]]; then for reg_trail in $TRAILS_AND_REGIONS; do TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1) diff --git a/checks/check25 b/checks/check25 index bdeaabba..010e8e3f 100644 --- a/checks/check25 +++ b/checks/check25 @@ -21,11 +21,20 @@ CHECK_SERVICENAME_check25="configservice" check25(){ # "Ensure AWS Config is enabled in all regions (Scored)" for regx in $REGIONS; do - CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice get-status $PROFILE_OPT --region $regx --output json| grep "recorder: ON") - if [[ $CHECK_AWSCONFIG_STATUS ]];then - textPass "Region $regx has AWS Config recorder: ON" "$regx" + CHECK_AWSCONFIG_RECORDING=$($AWSCLI configservice describe-configuration-recorder-status $PROFILE_OPT --region $regx --query 'ConfigurationRecordersStatus[*].recording' --output text 2>&1) + CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice describe-configuration-recorder-status $PROFILE_OPT --region $regx --query 'ConfigurationRecordersStatus[*].lastStatus' --output text 2>&1) + if [[ $(echo "$CHECK_AWSCONFIG_STATUS" | grep AccessDenied) ]]; then + textFail "Access Denied trying to describe configuration recorder status in $regx" + continue + fi + if [[ $CHECK_AWSCONFIG_RECORDING == "True" ]]; then + if [[ $CHECK_AWSCONFIG_STATUS == "SUCCESS" ]]; then + textPass "Region $regx AWS Config recorder enabled" + else + textFail "Region $regx AWS Config recorder in failure state" + fi else - textFail "Region $regx has AWS Config disabled or not configured" "$regx" + textFail "Region $regx AWS Config recorder disabled" fi done } diff --git a/checks/check26 b/checks/check26 index 8b7c5fd4..47d791d4 100644 --- a/checks/check26 +++ b/checks/check26 @@ -19,37 +19,68 @@ CHECK_ALTERNATE_check206="check26" CHECK_SERVICENAME_check26="s3" check26(){ + trail_count=0 # "Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" - - CLOUDTRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].Name' --output text| tr '\011' '\012' | awk -F: '{print $1}') - - if [[ $CLOUDTRAILS ]]; then - for trail in $CLOUDTRAILS; do - CLOUDTRAIL_ACCOUNT_ID=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].TrailARN' --output text | tr '\011' '\012' | grep "$trail" | awk -F: '{ print $5 }' | head -n 1) - CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].[Name, S3BucketName]' --output text | tr '\011' ':' | grep "$trail" | awk -F: '{ print $2 }' ) - - if [[ $CLOUDTRAILBUCKET ]]; then - bucket=$CLOUDTRAILBUCKET - if [ "$CLOUDTRAIL_ACCOUNT_ID" == "$ACCOUNT_NUM" ]; then - CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --region $REGION --query 'LoggingEnabled.TargetBucket' 2>&1) - if [[ $(echo "$CLOUDTRAILBUCKET_LOGENABLED" | grep AccessDenied) ]]; then - textInfo "Access Denied Trying to Get Bucket Logging for $bucket" - continue - fi - if [[ $CLOUDTRAILBUCKET_LOGENABLED != "null" ]]; then - textPass "Bucket access logging enabled in CloudTrail S3 bucket $bucket for $trail" - else - textFail "Bucket access logging is not enabled in CloudTrail S3 bucket $bucket for $trail" - fi - else - textInfo "CloudTrail S3 bucket $bucket for trail $trail is not in current account" + for regx in $REGIONS; do + TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',') + if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then + textFail "Access Denied trying to describe trails in $regx" + continue + fi + if [[ $TRAILS_AND_REGIONS ]]; then + for reg_trail in $TRAILS_AND_REGIONS; do + TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1) + if [ $TRAIL_REGION != $regx ]; then # Only report trails once in home region + continue fi - else - textFail "CloudTrail bucket not found!" - fi - done + trail=$(echo $reg_trail | cut -d',' -f2) + trail_count=$((trail_count + 1)) - else - textFail "No CloudWatch group found and no CloudTrail bucket" + CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $TRAIL_REGION --query 'trailList[*].[S3BucketName]' --output text --trail-name-list $trail) + if [[ -z $CLOUDTRAILBUCKET ]]; then + textFail "Trail $trail in $TRAIL_REGION does not publish to S3" + continue + fi + + CLOUDTRAIL_ACCOUNT_ID=$(echo $trail | awk -F: '{ print $5 }') + if [ "$CLOUDTRAIL_ACCOUNT_ID" != "$ACCOUNT_NUM" ]; then + textInfo "Trail $trail in $TRAIL_REGION S3 logging bucket $CLOUDTRAILBUCKET is not in current account" + continue + fi + + # + # LOCATION - requests referencing buckets created after March 20, 2019 + # must be made to S3 endpoints in the same region as the bucket was + # created. + # + BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location $PROFILE_OPT --region $regx --bucket $CLOUDTRAILBUCKET --output text 2>&1) + if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then + textFail "Trail $trail in $TRAIL_REGION Access Denied getting bucket location for $CLOUDTRAILBUCKET" + continue + fi + if [[ $BUCKET_LOCATION == "None" ]]; then + BUCKET_LOCATION="us-east-1" + fi + if [[ $BUCKET_LOCATION == "EU" ]]; then + BUCKET_LOCATION="eu-west-1" + fi + + CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $CLOUDTRAILBUCKET $PROFILE_OPT --region $BUCKET_LOCATION --query 'LoggingEnabled.TargetBucket' --output text 2>&1) + if [[ $(echo "$CLOUDTRAILBUCKET_LOGENABLED" | grep AccessDenied) ]]; then + textInfo "Trail $trail in $TRAIL_REGION Access Denied getting bucket logging for $CLOUDTRAILBUCKET" + continue + fi + + if [[ $CLOUDTRAILBUCKET_LOGENABLED != "None" ]]; then + textPass "Trail $trail in $TRAIL_REGION S3 bucket access logging is enabled for $CLOUDTRAILBUCKET" + else + textFail "Trail $trail in $TRAIL_REGION S3 bucket access logging is not enabled for $CLOUDTRAILBUCKET" + fi + + done + fi + done + if [[ $trail_count == 0 ]]; then + textFail "No CloudTrail trails were found in the account" fi } diff --git a/checks/check27 b/checks/check27 index bba7a604..927da424 100644 --- a/checks/check27 +++ b/checks/check27 @@ -23,7 +23,11 @@ check27(){ trail_count=0 # "Ensure CloudTrail logs are encrypted at rest using KMS CMKs (Scored)" for regx in $REGIONS; do - TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text | tr " " ',') + TRAILS_AND_REGIONS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].{Name:TrailARN, HomeRegion:HomeRegion}' --output text 2>&1 | tr " " ',') + if [[ $(echo "$TRAILS_AND_REGIONS" | grep AccessDenied) ]]; then + textFail "Access Denied trying to describe trails in $regx" + continue + fi if [[ $TRAILS_AND_REGIONS ]]; then for reg_trail in $TRAILS_AND_REGIONS; do TRAIL_REGION=$(echo $reg_trail | cut -d',' -f1) diff --git a/checks/check28 b/checks/check28 index a7af6afd..aeeeca70 100644 --- a/checks/check28 +++ b/checks/check28 @@ -21,31 +21,53 @@ CHECK_SERVICENAME_check28="kms" check28(){ # "Ensure rotation for customer created CMKs is enabled (Scored)" for regx in $REGIONS; do - CHECK_KMS_KEYLIST=$($AWSCLI kms list-keys $PROFILE_OPT --region $regx --output text --query 'Keys[*].KeyId') - if [[ $CHECK_KMS_KEYLIST ]];then - CHECK_KMS_KEYLIST_NO_DEFAULT=$( - for key in $CHECK_KMS_KEYLIST; do - $AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --query 'KeyMetadata.{key:KeyId,state:KeyState,man:KeyManager,spec:CustomerMasterKeySpec}' --output text|grep Enabled$|grep -v AWS|grep SYMMETRIC| awk '{ print $1 }' - done ) - if [[ $CHECK_KMS_KEYLIST_NO_DEFAULT ]]; then - for key in $CHECK_KMS_KEYLIST_NO_DEFAULT; do - CHECK_KMS_KEY_TYPE=$($AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --query 'KeyMetadata.Origin' | sed 's/["]//g') - if [[ "$CHECK_KMS_KEY_TYPE" == "EXTERNAL" ]];then - textPass "$regx: Key $key in Region $regx Customer Uploaded Key Material" "$regx" - else - CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key $PROFILE_OPT --region $regx --output text) - if [[ "$CHECK_KMS_KEY_ROTATION" == "True" ]];then - textPass "$regx: Key $key is set correctly" "$regx" - else - textFail "$regx: Key $key is not set to rotate!" "$regx" - fi - fi - done - else - textInfo "$regx: This region doesn't have CUSTOM encryption keys" "$regx" + CHECK_KMS_KEYLIST=$($AWSCLI kms list-keys $PROFILE_OPT --region $regx --output text --query 'Keys[*].KeyId' --output text 2>&1) + if [[ $(echo "$CHECK_KMS_KEYLIST" | grep AccessDenied) ]]; then + textFail "Access Denied trying to list keys in $regx" + continue + fi + if [[ $CHECK_KMS_KEYLIST ]]; then + cmk_count=0 + for key in $CHECK_KMS_KEYLIST; do + KMSDETAILS=$($AWSCLI kms describe-key --key-id $key $PROFILE_OPT --region $regx --query 'KeyMetadata.{key:KeyId,state:KeyState,man:KeyManager,spec:CustomerMasterKeySpec}' --output text 2>&1|grep Enabled$|grep -v AWS|grep SYMMETRIC| awk '{ print $1 }') + if [[ $(echo "$KMSDETAILS" | grep AccessDenied) ]]; then + textFail "$regx: Key $key Access Denied describing key" + continue + fi + + KEYID=$(echo $KMSDETAILS | awk '{print $1}') + KEYMANAGER=$(echo $KMSDETAILS | awk '{print $2}') + KEYORIGIN=$(echo $KMSDETAILS | awk '{print $3}') + KEYSTATE=$(echo $KMSDETAILS | awk '{print $4}') + + if [[ "$KEYMANAGER" == "AWS" ]]; then + continue + fi + if [[ "$KEYSTATE" != "Enabled" ]]; then + continue + fi + cmk_count=$((cmk_count + 1)) + + if [[ "$KEYORIGIN" == "EXTERNAL" ]]; then + textPass "$regx: Key $key uses imported key material" + else + CHECK_KMS_KEY_ROTATION=$($AWSCLI kms get-key-rotation-status --key-id $key $PROFILE_OPT --region $regx --output text 2>&1) + if [[ $(echo "$CHECK_KMS_KEY_ROTATION" | grep AccessDenied) ]]; then + textFail "$regx: Key $key Access Denied getting key rotation status" + continue + fi + if [[ "$CHECK_KMS_KEY_ROTATION" == "True" ]];then + textPass "$regx: Key $key automatic rotation of the key material is enabled" + else + textFail "$regx: Key $key automatic rotation of the key material is disabled" + fi + fi + done + if [[ $cmk_count == 0 ]]; then + textInfo "$regx: This region has no customer managed keys" fi else - textInfo "$regx: This region doesn't have ANY encryption keys" "$regx" + textInfo "$regx: This region has no KMS keys" fi done } diff --git a/checks/check29 b/checks/check29 index 9c93d50a..c71571ef 100644 --- a/checks/check29 +++ b/checks/check29 @@ -22,15 +22,23 @@ CHECK_SERVICENAME_check29="vpc" check29(){ # "Ensure VPC Flow Logging is Enabled in all VPCs (Scored)" for regx in $REGIONS; do - AVAILABLE_VPC=$($AWSCLI ec2 describe-vpcs $PROFILE_OPT --region $regx --query 'Vpcs[?State==`available`].VpcId' --output text) + AVAILABLE_VPC=$($AWSCLI ec2 describe-vpcs $PROFILE_OPT --region $regx --query 'Vpcs[?State==`available`].VpcId' --output text 2>&1) + if [[ $(echo "$AVAILABLE_VPC" | grep AccessDenied) ]]; then + textFail "$regx: Access Denied trying to describe VPCs" + continue + fi for vpcx in $AVAILABLE_VPC; do - CHECK_FL=$($AWSCLI ec2 describe-flow-logs $PROFILE_OPT --region $regx --filter Name="resource-id",Values="${vpcx}" --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].FlowLogId' --output text) - if [[ $CHECK_FL ]];then - for FL in $CHECK_FL;do - textPass "VPC $vpcx: VPCFlowLog is enabled for LogGroupName: $FL in Region $regx" "$regx" + CHECK_FL=$($AWSCLI ec2 describe-flow-logs $PROFILE_OPT --region $regx --filter Name="resource-id",Values="${vpcx}" --query 'FlowLogs[?FlowLogStatus==`ACTIVE`].FlowLogId' --output text 2>&1) + if [[ $(echo "$CHECK_FL" | grep AccessDenied) ]]; then + textFail "$regx: VPC $vpcx Access Denied trying to describe flow logs" + continue + fi + if [[ $CHECK_FL ]]; then + for FL in $CHECK_FL; do + textPass "$regx: VPC $vpcx VPCFlowLog is enabled for LogGroupName: $FL" done else - textFail "VPC $vpcx: No VPCFlowLog has been found in Region $regx" "$regx" + textFail "$regx: VPC $vpcx VPCFlowLog is disabled" fi done done diff --git a/checks/check43 b/checks/check43 index fa5d18f5..1742e675 100644 --- a/checks/check43 +++ b/checks/check43 @@ -12,7 +12,7 @@ CHECK_ID_check43="4.3" CHECK_TITLE_check43="[check43] Ensure the default security group of every VPC restricts all traffic (Scored)" CHECK_SCORED_check43="SCORED" CHECK_TYPE_check43="LEVEL2" -CHECK_SEVERITY_check43="Medium" +CHECK_SEVERITY_check43="High" CHECK_ASFF_TYPE_check43="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark" CHECK_ASFF_RESOURCE_TYPE_check43="AwsEc2SecurityGroup" CHECK_ALTERNATE_check403="check43" diff --git a/checks/check_extra7102 b/checks/check_extra7102 index 8f1cb17a..3be4629e 100644 --- a/checks/check_extra7102 +++ b/checks/check_extra7102 @@ -14,7 +14,7 @@ CHECK_ID_extra7102="7.102" CHECK_TITLE_extra7102="[extra7102] Check if any of the Elastic or Public IP are in Shodan (requires Shodan API KEY)" CHECK_SCORED_extra7102="NOT_SCORED" CHECK_TYPE_extra7102="EXTRA" -CHECK_SEVERITY_extra7102="Medium" +CHECK_SEVERITY_extra7102="High" CHECK_ASFF_RESOURCE_TYPE_extra7102="AwsEc2Eip" CHECK_ALTERNATE_check7102="extra7102" CHECK_SERVICENAME_extra7102="ec2" diff --git a/checks/check_extra7127 b/checks/check_extra7127 index 65566690..9f28c605 100644 --- a/checks/check_extra7127 +++ b/checks/check_extra7127 @@ -14,7 +14,7 @@ CHECK_ID_extra7127="7.127" CHECK_TITLE_extra7127="[extra7127] Check if EC2 instances managed by Systems Manager are compliant with patching requirements" CHECK_SCORED_extra7127="NOT_SCORED" CHECK_TYPE_extra7127="EXTRA" -CHECK_SEVERITY_extra7127="Medium" +CHECK_SEVERITY_extra7127="High" CHECK_ASFF_RESOURCE_TYPE_extra7127="AwsEc2Instance" CHECK_ASFF_TYPE_extra7127="Software and Configuration Checks/ENS op.exp.4.aws.sys.1" CHECK_ALTERNATE_check7127="extra7127" diff --git a/checks/check_extra7132 b/checks/check_extra7132 new file mode 100644 index 00000000..8a704100 --- /dev/null +++ b/checks/check_extra7132 @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy +# of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +CHECK_ID_extra7132="7.132" +CHECK_TITLE_extra7132="[extra7132] Check if RDS instances has enhanced monitoring enabled" +CHECK_SCORED_extra7132="NOT_SCORED" +CHECK_TYPE_extra7132="EXTRA" +CHECK_SEVERITY_extra7132="Low" +CHECK_ASFF_RESOURCE_TYPE_extra7132="AwsRdsDbInstance" +CHECK_ALTERNATE_check7132="extra7132" +CHECK_SERVICENAME_extra7132="rds" + +extra7132(){ + for regx in $REGIONS; do + RDS_INSTANCES=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --query 'DBInstances[*].DBInstanceIdentifier' --output text) + if [[ $RDS_INSTANCES ]];then + for rdsinstance in ${RDS_INSTANCES}; do + RDS_NAME="$rdsinstance" + MONITORING_FLAG=$($AWSCLI rds describe-db-instances $PROFILE_OPT --region $regx --db-instance-identifier $rdsinstance --query 'DBInstances[*].[EnhancedMonitoringResourceArn]' --output text) + if [[ $MONITORING_FLAG == "None" ]];then + textFail "$regx: RDS instance: $RDS_NAME has enhanced monitoring disabled!" "$rex" + else + textPass "$regx: RDS instance: $RDS_NAME has enhanced monitoring enabled." "$regx" + fi + done + else + textInfo "$regx: no RDS instances found" "$regx" + fi + done +} diff --git a/checks/check_extra720 b/checks/check_extra720 index 8e0647fd..f36ab448 100644 --- a/checks/check_extra720 +++ b/checks/check_extra720 @@ -22,10 +22,18 @@ CHECK_SERVICENAME_extra720="lambda" extra720(){ # "Check if Lambda functions invoke API operations are being recorded by CloudTrail (Not Scored) (Not part of CIS benchmark)" for regx in $REGIONS; do - LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --query Functions[*].FunctionName --output text) + LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --query 'Functions[*].FunctionName' --output text 2>&1) + if [[ $(echo "$LIST_OF_FUNCTIONS" | grep AccessDenied) ]]; then + textFail "$regx: Access Denied trying to list functions" + continue + fi if [[ $LIST_OF_FUNCTIONS ]]; then - for lambdafunction in $LIST_OF_FUNCTIONS;do - LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query trailList[].TrailARN --output text) + LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].TrailARN' --output text 2>&1) + if [[ $(echo "$LIST_OF_TRAILS" | grep AccessDenied) ]]; then + textFail "$regx: Access Denied trying to describe trails" + continue + fi + for lambdafunction in $LIST_OF_FUNCTIONS; do if [[ $LIST_OF_TRAILS ]]; then for trail in $LIST_OF_TRAILS; do FUNCTION_ENABLED_IN_TRAIL=$($AWSCLI cloudtrail get-event-selectors $PROFILE_OPT --trail-name $trail --region $regx --query "EventSelectors[*].DataResources[?Type == \`AWS::Lambda::Function\`].Values" --output text |xargs -n1| grep -E "^arn:${AWS_PARTITION}:lambda.*function:$lambdafunction$|^arn:${AWS_PARTITION}:lambda$") @@ -52,7 +60,7 @@ extra720(){ else textFail "$regx: Lambda function $lambdafunction is not being recorded no CloudTrail found!" "$regx" fi - done + done else textInfo "$regx: No Lambda functions found" "$regx" fi diff --git a/checks/check_extra725 b/checks/check_extra725 index 28d2557f..d12fd367 100644 --- a/checks/check_extra725 +++ b/checks/check_extra725 @@ -26,19 +26,25 @@ extra725(){ # "Check if S3 buckets have Object-level logging enabled in CloudTrail (Not Scored) (Not part of CIS benchmark)" textInfo "Looking for S3 Buckets Object-level logging information in all trails... " - LIST_OF_BUCKETS=$($AWSCLI s3api list-buckets $PROFILE_OPT --region $REGION --query 'Buckets[*].{Name:Name}' --output text) - LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[].TrailARN' --output text) + LIST_OF_BUCKETS=$($AWSCLI s3api list-buckets $PROFILE_OPT --region $REGION --query 'Buckets[*].{Name:Name}' --output text 2>&1) + if [[ $(echo "$LIST_OF_BUCKETS" | grep AccessDenied) ]]; then + textFail "Access Denied trying to list buckets" + return + fi + LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[].TrailARN' --output text 2>&1) + if [[ $(echo "$LIST_OF_TRAILS" | grep AccessDenied) ]]; then + textFail "Access Denied trying to describe trails" + return + fi if [[ $LIST_OF_BUCKETS ]]; then - for bucketName in $LIST_OF_BUCKETS;do + for bucketName in $LIST_OF_BUCKETS; do if [[ $LIST_OF_TRAILS ]]; then BUCKET_ENABLED_TRAILS=() + for trail in $LIST_OF_TRAILS; do BUCKET_ENABLED_IN_TRAIL=$($AWSCLI cloudtrail get-event-selectors --region $REGION $PROFILE_OPT --trail-name $trail --query "EventSelectors[*].DataResources[?Type == \`AWS::S3::Object\`].Values" --output text |xargs -n1| grep -E "^arn:${AWS_PARTITION}:s3:::$bucketName/\S*$|^arn:${AWS_PARTITION}:s3$|^arn:${AWS_PARTITION}:s3:::$") if [[ $BUCKET_ENABLED_IN_TRAIL ]]; then BUCKET_ENABLED_TRAILS+=($trail) - # textPass "$regx: S3 bucket $bucketName has Object-level logging enabled in trail $trail" "$regx" - #else - # textFail "$regx: S3 bucket $bucketName has Object-level logging disabled" "$regx" fi done diff --git a/checks/check_extra741 b/checks/check_extra741 index 7643e512..d0501ce9 100644 --- a/checks/check_extra741 +++ b/checks/check_extra741 @@ -14,7 +14,7 @@ CHECK_ID_extra741="7.41" CHECK_TITLE_extra741="[extra741] Find secrets in EC2 User Data (Not Scored) (Not part of CIS benchmark)" CHECK_SCORED_extra741="NOT_SCORED" CHECK_TYPE_extra741="EXTRA" -CHECK_SEVERITY_extra741="Medium" +CHECK_SEVERITY_extra741="Critical" CHECK_ASFF_RESOURCE_TYPE_extra741="AwsEc2Instance" CHECK_ALTERNATE_check741="extra741" CHECK_SERVICENAME_extra741="ec2" diff --git a/checks/check_extra742 b/checks/check_extra742 index 1aa2adda..957ef0c2 100644 --- a/checks/check_extra742 +++ b/checks/check_extra742 @@ -14,7 +14,7 @@ CHECK_ID_extra742="7.42" CHECK_TITLE_extra742="[extra742] Find secrets in CloudFormation outputs (Not Scored) (Not part of CIS benchmark)" CHECK_SCORED_extra742="NOT_SCORED" CHECK_TYPE_extra742="EXTRA" -CHECK_SEVERITY_extra742="Medium" +CHECK_SEVERITY_extra742="Critical" CHECK_ASFF_RESOURCE_TYPE_extra742="AwsCloudFormationStack" CHECK_ALTERNATE_check742="extra742" CHECK_SERVICENAME_extra742="cloudformation" diff --git a/checks/check_extra759 b/checks/check_extra759 index 4414712b..bf4d8052 100644 --- a/checks/check_extra759 +++ b/checks/check_extra759 @@ -14,7 +14,7 @@ CHECK_ID_extra759="7.59" CHECK_TITLE_extra759="[extra759] Find secrets in Lambda functions variables (Not Scored) (Not part of CIS benchmark)" CHECK_SCORED_extra759="NOT_SCORED" CHECK_TYPE_extra759="EXTRA" -CHECK_SEVERITY_extra759="High" +CHECK_SEVERITY_extra759="Critical" CHECK_ASFF_RESOURCE_TYPE_extra759="AwsLambdaFunction" CHECK_ALTERNATE_check759="extra759" CHECK_SERVICENAME_extra759="lambda" diff --git a/checks/check_extra760 b/checks/check_extra760 index 5a3b0ece..ba54aa89 100644 --- a/checks/check_extra760 +++ b/checks/check_extra760 @@ -14,7 +14,7 @@ CHECK_ID_extra760="7.60" CHECK_TITLE_extra760="[extra760] Find secrets in Lambda functions code (Not Scored) (Not part of CIS benchmark)" CHECK_SCORED_extra760="NOT_SCORED" CHECK_TYPE_extra760="EXTRA" -CHECK_SEVERITY_extra760="Medium" +CHECK_SEVERITY_extra760="Critical" CHECK_ASFF_RESOURCE_TYPE_extra760="AwsLambdaFunction" CHECK_ALTERNATE_check760="extra760" CHECK_SERVICENAME_extra760="lambda" diff --git a/checks/check_extra768 b/checks/check_extra768 index e82b98a5..25078fd8 100644 --- a/checks/check_extra768 +++ b/checks/check_extra768 @@ -14,7 +14,7 @@ CHECK_ID_extra768="7.68" CHECK_TITLE_extra768="[extra768] Find secrets in ECS task definitions variables (Not Scored) (Not part of CIS benchmark)" CHECK_SCORED_extra768="NOT_SCORED" CHECK_TYPE_extra768="EXTRA" -CHECK_SEVERITY_extra768="Medium" +CHECK_SEVERITY_extra768="Critical" CHECK_ASFF_RESOURCE_TYPE_extra768="AwsEcsTaskDefinition" CHECK_ALTERNATE_check768="extra768" CHECK_SERVICENAME_extra768="ecs" diff --git a/checks/check_extra775 b/checks/check_extra775 index 1cbefab0..5a60b320 100644 --- a/checks/check_extra775 +++ b/checks/check_extra775 @@ -14,7 +14,7 @@ CHECK_ID_extra775="7.75" CHECK_TITLE_extra775="[extra775] Find secrets in EC2 Auto Scaling Launch Configuration (Not Scored) (Not part of CIS benchmark)" CHECK_SCORED_extra775="NOT_SCORED" CHECK_TYPE_extra775="EXTRA" -CHECK_SEVERITY_extra775="Medium" +CHECK_SEVERITY_extra775="Critical" CHECK_ALTERNATE_check775="extra775" CHECK_SERVICENAME_extra775="autoscaling" diff --git a/groups/group13_rds b/groups/group13_rds index e684654b..e4fa3079 100644 --- a/groups/group13_rds +++ b/groups/group13_rds @@ -15,4 +15,4 @@ GROUP_ID[13]='rds' GROUP_NUMBER[13]='13.0' GROUP_TITLE[13]='RDS security checks - [rds] ***********************************' GROUP_RUN_BY_DEFAULT[13]='N' # run it when execute_all is called -GROUP_CHECKS[13]='extra78,extra723,extra735,extra739,extra747,extra7113,extra7131' +GROUP_CHECKS[13]='extra78,extra723,extra735,extra739,extra747,extra7113,extra7131,extra7132' diff --git a/groups/group7_extras b/groups/group7_extras index e95fd308..512a3744 100644 --- a/groups/group7_extras +++ b/groups/group7_extras @@ -15,7 +15,7 @@ GROUP_ID[7]='extras' GROUP_NUMBER[7]='7.0' GROUP_TITLE[7]='Extras - all non CIS specific checks - [extras] ****************' GROUP_RUN_BY_DEFAULT[7]='Y' # run it when execute_all is called -GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra737,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra756,extra757,extra758,extra761,extra762,extra763,extra764,extra765,extra767,extra768,extra769,extra770,extra771,extra772,extra773,extra774,extra775,extra776,extra777,extra778,extra779,extra780,extra781,extra782,extra783,extra784,extra785,extra786,extra787,extra788,extra791,extra792,extra793,extra794,extra795,extra796,extra797,extra798,extra799,extra7100,extra7101,extra7102,extra7103,extra7104,extra7105,extra7106,extra7107,extra7108,extra7109,extra7110,extra7111,extra7112,extra7113,extra7114,extra7115,extra7116,extra7117,extra7118,extra7119,extra7120,extra7121,extra7122,extra7123,extra7124,extra7125,extra7126,extra7127,extra7128,extra7129,extra7130,extra7131' +GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra737,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra756,extra757,extra758,extra761,extra762,extra763,extra764,extra765,extra767,extra768,extra769,extra770,extra771,extra772,extra773,extra774,extra775,extra776,extra777,extra778,extra779,extra780,extra781,extra782,extra783,extra784,extra785,extra786,extra787,extra788,extra791,extra792,extra793,extra794,extra795,extra796,extra797,extra798,extra799,extra7100,extra7101,extra7102,extra7103,extra7104,extra7105,extra7106,extra7107,extra7108,extra7109,extra7110,extra7111,extra7112,extra7113,extra7114,extra7115,extra7116,extra7117,extra7118,extra7119,extra7120,extra7121,extra7122,extra7123,extra7124,extra7125,extra7126,extra7127,extra7128,extra7129,extra7130,extra7131,extra7132' # Extras 759 and 760 (lambda variables and code secrets finder are not included) # to run detect-secrets use `./prowler -g secrets` diff --git a/prowler b/prowler index 7f0698d3..b31a8d2f 100755 --- a/prowler +++ b/prowler @@ -51,6 +51,15 @@ TITLE_TEXT="CALLER ERROR - UNSET TITLE" WHITELIST_FILE="" TOTAL_CHECKS=() +# Ensures command output will always be set to JSON. +# If the default value is already set, ORIGINAL_OUTPUT will be used to store it and reset it at cleanup +if [[ -z "${AWS_DEFAULT_OUTPUT}" ]]; then + ORIGINAL_OUTPUT=$AWS_DEFAULT_OUTPUT + export AWS_DEFAULT_OUTPUT="json" +else + export AWS_DEFAULT_OUTPUT="json" +fi + # Command usage menu usage(){ echo " @@ -204,6 +213,12 @@ clean_up() { if [[ "${MODES[@]}" =~ "html" ]]; then addHtmlFooter >> ${OUTPUT_FILE_NAME}.$EXTENSION_HTML fi + # puts the AWS_DEFAULT_OUTPUT back to what it was at the start + if [ -z "$ORIGINAL_OUTPUT"]; then + export AWS_DEFAULT_OUTPUT="$ORIGINAL_OUTPUT" + else + unset AWS_DEFAULT_OUTPUT + fi } handle_ctrl_c() {