diff --git a/Pipfile b/Pipfile index 3f64ab1980..836b168cd9 100644 --- a/Pipfile +++ b/Pipfile @@ -6,9 +6,9 @@ verify_ssl = true [dev-packages] [packages] -boto3="1.9.188" -ansi2html="1.5.2" -detect-secrets="0.12.4" +boto3 = ">=1.9.188" +ansi2html = ">=1.5.2" +detect-secrets = ">=0.12.4" [requires] python_version = "3.7" diff --git a/README.md b/README.md index 6e08cf351a..70e8771655 100644 --- a/README.md +++ b/README.md @@ -55,13 +55,13 @@ With Prowler you can: This script has been written in bash using AWS-CLI and it works in Linux and OSX. -- Make sure your AWS-CLI is installed on your workstation, with Python pip already installed: +- Make sure your AWS-CLI is installed on your workstation, and other components needed, with Python pip already installed: ```sh - pip install awscli + pip install awscli ansi2html detect-secrets ``` - Or install it using "brew", "apt", "yum" or manually from + AWS-CLI can be also installed it using "brew", "apt", "yum" or manually from , but `ansi2html` and `detect-secrets` has to be installed using `pip` - Previous steps, from your workstation: @@ -125,6 +125,10 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX ```sh ./prowler -g group1 # for iam related checks ``` + or exclude some checks in the group: + ```sh + ./prowler -g group4 -E check42,check43 + ``` Valid check numbers are based on the AWS CIS Benchmark guide, so 1.1 is check11 and 3.10 is check310 @@ -239,11 +243,11 @@ aws --profile sts get-session-token --duration 129600 --seria ``` Once you get your token you can export it as environment variable: ``` -export AWS_PROFILE=YOUR_AWS_PROFILE +export AWS_PROFILE=YOUR_AWS_PROFILE export AWS_SESSION_TOKEN=YOUR_NEW_TOKEN AWS_SECRET_ACCESS_KEY=YOUR_SECRET export AWS_ACCESS_KEY_ID=YOUR_KEY -``` +``` or set manually up your `~/.aws/credentials` file properly. There are some helpfull tools to save time in this process like [aws-mfa-script](https://github.com/asagage/aws-mfa-script) or [aws-cli-mfa](https://github.com/sweharris/aws-cli-mfa). @@ -306,6 +310,7 @@ Instead of using default policy SecurityAudit for the account you use for checks "firehose:describe*", "firehose:list*", "glacier:listvaults", + "guardduty:GetDetector", "guardduty:listdetectors", "iam:generatecredentialreport", "iam:get*", @@ -407,7 +412,7 @@ aws iam create-access-key --user-name prowler unset ACCOUNT_ID AWS_DEFAULT_PROFILE ``` -The `aws iam create-access-key` command will output the secret access key and the key id; keep these somewhere safe, and add them to `~/.aws/credentials` with an appropriate profile name to use them with prowler. This is the only time they secret key will be shown. If you loose it, you will need to generate a replacement. +The `aws iam create-access-key` command will output the secret access key and the key id; keep these somewhere safe, and add them to `~/.aws/credentials` with an appropriate profile name to use them with prowler. This is the only time they secret key will be shown. If you lose it, you will need to generate a replacement. ## Extras diff --git a/checks/check120 b/checks/check120 index c8f129c8de..2983642faa 100644 --- a/checks/check120 +++ b/checks/check120 @@ -19,10 +19,10 @@ check120(){ SUPPORTPOLICYARN=$($AWSCLI iam list-policies --query "Policies[?PolicyName == 'AWSSupportAccess'].Arn" $PROFILE_OPT --region $REGION --output text) if [[ $SUPPORTPOLICYARN ]];then for policyarn in $SUPPORTPOLICYARN;do - POLICYROLES=$($AWSCLI iam list-entities-for-policy --policy-arn $SUPPORTPOLICYARN $PROFILE_OPT --region $REGION --query PolicyRoles[*] --output text) + POLICYROLES=$($AWSCLI iam list-entities-for-policy --policy-arn $SUPPORTPOLICYARN $PROFILE_OPT --region $REGION --output text | awk -F$'\t' '{ print $3 }') if [[ $POLICYROLES ]];then - for role in $POLICYROLES; do - textPass "Support Policy attached to $role role" + for name in $POLICYROLES; do + textPass "Support Policy attached to $name" done # for user in $(echo "$POLICYUSERS" | grep UserName | cut -d'"' -f4) ; do # textInfo "User $user has support access via $policyarn" diff --git a/checks/check122 b/checks/check122 index 2670a9b57f..0ae9e6c34b 100644 --- a/checks/check122 +++ b/checks/check122 @@ -22,7 +22,7 @@ check122(){ for policy in $LIST_CUSTOM_POLICIES; do POLICY_ARN=$(echo $policy | awk -F ',' '{print $1}') POLICY_VERSION=$(echo $policy | awk -F ',' '{print $2}') - POLICY_WITH_FULL=$($AWSCLI iam get-policy-version --output text --policy-arn $POLICY_ARN --version-id $POLICY_VERSION --query "PolicyVersion.Document.Statement[?Action!=null]|[?Effect == 'Allow' && Resource == '*' && Action == '*']" $PROFILE_OPT --region $REGION) + POLICY_WITH_FULL=$($AWSCLI iam get-policy-version --output text --policy-arn $POLICY_ARN --version-id $POLICY_VERSION --query "[PolicyVersion.Document.Statement] | [] | [?Action!=null] | [?Effect == 'Allow' && Resource == '*' && Action == '*']" $PROFILE_OPT --region $REGION) if [[ $POLICY_WITH_FULL ]]; then POLICIES_ALLOW_LIST="$POLICIES_ALLOW_LIST $POLICY_ARN" fi diff --git a/checks/check26 b/checks/check26 index 5d19c2c6e4..133a4176d8 100644 --- a/checks/check26 +++ b/checks/check26 @@ -16,17 +16,34 @@ CHECK_ALTERNATE_check206="check26" check26(){ # "Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket (Scored)" - CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text $PROFILE_OPT --region $REGION) - if [[ $CLOUDTRAILBUCKET ]];then - for bucket in $CLOUDTRAILBUCKET;do - CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None) + + 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_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].TrailARN' --output text | tr '\011' '\012' | grep "$trail" | awk -F: '{ print $4 }' | head -n 1) + 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_ID" ];then + CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None) + fi if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then - textPass "Bucket access logging enabled in $bucket" - else - textFail "access logging is not enabled in $bucket CloudTrail S3 bucket!" + textPass "Bucket access logging enabled in CloudTrail S3 bucket $bucket for $trail" + elif [ "$CLOUDTRAIL_ACCOUNT_ID" == "$ACCOUNT_ID" ];then + textFail "Bucket access logging is not enabled in CloudTrail S3 bucket $bucket for $trail" + else + textInfo "CloudTrail S3 bucket $bucket for trail $trail is not in current account" fi - done - else - textFail "CloudTrail bucket not found!" - fi + + else + textFail "CloudTrail bucket not found!" + fi + done + + else + textFail "No CloudWatch group found and no CloudTrail bucket" + fi } diff --git a/checks/check31 b/checks/check31 index 2b7cb597b0..13a11eb1e4 100644 --- a/checks/check31 +++ b/checks/check31 @@ -15,49 +15,5 @@ CHECK_TYPE_check31="LEVEL1" CHECK_ALTERNATE_check301="check31" check31(){ - # "Ensure a log metric filter and alarm exist for unauthorized API calls (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text| tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - #METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | awk '/UnauthorizedOperation/ || /AccessDenied/ {print $3}') - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --output text | grep METRICFILTERS | awk 'BEGIN {IGNORECASE=1}; /UnauthorizedOperation/ || /AccessDenied/ {print $3};') - if [[ $METRICFILTER_SET ]];then - for metric in $METRICFILTER_SET; do - metric_name=$($AWSCLI logs describe-metric-filters $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --log-group-name $group --filter-name-prefix $metric --output text --query 'metricFilters[0].metricTransformations[0].metricName') - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[?MetricName==`'$metric_name'`]' --output text) - if [[ $HAS_ALARM_ASSOCIATED ]];then - CHECK31OK="$CHECK31OK $group:$metric" - else - CHECK31WARN="$CHECK31WARN $group:$metric" - fi - done - else - CHECK31WARN="$CHECK31WARN $group" - fi - done - - if [[ $CHECK31OK ]]; then - for group in $CHECK31OK; do - metric=${group#*:} - group=${group%:*} - textPass "CloudWatch group $group found with metric filter $metric and alarms set for Unauthorized Operation and Access Denied" - done - fi - if [[ $CHECK31WARN ]]; then - for group in $CHECK31WARN; do - case $group in - *:*) metric=${group#*:} - group=${group%:*} - textFail "CloudWatch group $group found with metric filter $metric but no alarms associated" - ;; - *) textFail "CloudWatch group $group found but no metric filters or alarms associated" - esac - done - fi - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.errorCode\s*=\s*"\*UnauthorizedOperation".+\$\.errorCode\s*=\s*"AccessDenied\*"' } diff --git a/checks/check310 b/checks/check310 index 3f8f7db4e9..3c1e1d1124 100644 --- a/checks/check310 +++ b/checks/check310 @@ -15,26 +15,5 @@ CHECK_TYPE_check310="LEVEL2" CHECK_ALTERNATE_check310="check310" check310(){ - # "Ensure a log metric filter and alarm exist for security group changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'AuthorizeSecurityGroupIngress.*AuthorizeSecurityGroupEgress.*RevokeSecurityGroupIngress.*RevokeSecurityGroupEgress.*CreateSecurityGroup.*DeleteSecurityGroup') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /SecurityGroup/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for security group changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*CreateSecurityGroup.+\$\.eventName\s*=\s*DeleteSecurityGroup' } diff --git a/checks/check311 b/checks/check311 index 391b824c74..12aec273f4 100644 --- a/checks/check311 +++ b/checks/check311 @@ -15,26 +15,5 @@ CHECK_TYPE_check311="LEVEL2" CHECK_ALTERNATE_check311="check311" check311(){ - # "Ensure a log metric filter and alarm exist for changes to Network Access Control Lists (NACL) (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateNetworkAcl.*CreateNetworkAclEntry.*DeleteNetworkAcl.*DeleteNetworkAclEntry.*ReplaceNetworkAclEntry.*ReplaceNetworkAclAssociation') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /NetworkAcl/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for changes to NACLs" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateNetworkAcl.+\$\.eventName\s*=\s*CreateNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*ReplaceNetworkAclAssociation' } diff --git a/checks/check312 b/checks/check312 index a30987d628..0bf516b693 100644 --- a/checks/check312 +++ b/checks/check312 @@ -15,26 +15,5 @@ CHECK_TYPE_check312="LEVEL1" CHECK_ALTERNATE_check312="check312" check312(){ - # "Ensure a log metric filter and alarm exist for changes to network gateways (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateCustomerGateway.*DeleteCustomerGateway.*AttachInternetGateway.*CreateInternetGateway.*DeleteInternetGateway.*DetachInternetGateway') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /InternetGateway/ || /CustomerGateway/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for changes to network gateways" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateCustomerGateway.+\$\.eventName\s*=\s*DeleteCustomerGateway.+\$\.eventName\s*=\s*DeleteInternetGateway.+\$\.eventName\s*=\s*DetachInternetGateway' } diff --git a/checks/check313 b/checks/check313 index bccb8cbd66..ca619ecc72 100644 --- a/checks/check313 +++ b/checks/check313 @@ -15,26 +15,5 @@ CHECK_TYPE_check313="LEVEL1" CHECK_ALTERNATE_check313="check313" check313(){ - # "Ensure a log metric filter and alarm exist for route table changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateRoute.*CreateRouteTable.*ReplaceRoute.*ReplaceRouteTableAssociation.*DeleteRouteTable.*DeleteRoute.*DisassociateRouteTable') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /Route/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for route table changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateRoute.+\$\.eventName\s*=\s*CreateRouteTable.+\$\.eventName\s*=\s*DeleteRoute.+\$\.eventName\s*=\s*DisassociateRouteTable' } diff --git a/checks/check314 b/checks/check314 index bc870f3331..6c29c94632 100644 --- a/checks/check314 +++ b/checks/check314 @@ -15,26 +15,5 @@ CHECK_TYPE_check314="LEVEL1" CHECK_ALTERNATE_check314="check314" check314(){ - # "Ensure a log metric filter and alarm exist for VPC changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | grep $group | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateVpc.*DeleteVpc.*ModifyVpcAttribute.*AcceptVpcPeeringConnection.*CreateVpcPeeringConnection.*DeleteVpcPeeringConnection.*RejectVpcPeeringConnection.*AttachClassicLinkVpc.*DetachClassicLinkVpc.*DisableVpcClassicLink.*EnableVpcClassicLink') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /VPC/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for VPC changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateVpc.+\$\.eventName\s*=\s*DeleteVpc.+\$\.eventName\s*=\s*DisableVpcClassicLink.+\$\.eventName\s*=\s*EnableVpcClassicLink' } diff --git a/checks/check32 b/checks/check32 index ff7fc05e03..e13b18dd57 100644 --- a/checks/check32 +++ b/checks/check32 @@ -15,25 +15,5 @@ CHECK_TYPE_check32="LEVEL1" CHECK_ALTERNATE_check302="check32" check32(){ - # "Ensure a log metric filter and alarm exist for Management Console sign-in without MFA (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' |grep filterPattern|grep MFAUsed| awk '/ConsoleLogin/ && (/additionalEventData.MFAUsed.*\!=.*\"Yes/) {print $1}') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /ConsoleLogin/ || /MFAUsed/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms set for sign-in Console without MFA enabled" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*"ConsoleLogin".+\$\.additionalEventData\.MFAUsed\s*!=\s*"Yes"' } diff --git a/checks/check33 b/checks/check33 index 9d83b3dcdf..b6825566c0 100644 --- a/checks/check33 +++ b/checks/check33 @@ -15,25 +15,5 @@ CHECK_TYPE_check33="LEVEL1" CHECK_ALTERNATE_check303="check33" check33(){ - # "Ensure a log metric filter and alarm exist for usage of root account (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION |grep -E 'userIdentity.*Root.*AwsServiceEvent') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | tr '[:upper:]' '[:lower:]'| grep -Ei 'userIdentity|Root|AwsServiceEvent') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms set for usage of root account" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.userIdentity\.type\s*=\s*"Root".+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*"AwsServiceEvent"' } diff --git a/checks/check34 b/checks/check34 index 1883a823b1..4eaf345cd6 100644 --- a/checks/check34 +++ b/checks/check34 @@ -15,25 +15,5 @@ CHECK_TYPE_check34="LEVEL1" CHECK_ALTERNATE_check304="check34" check34(){ - # "Ensure a log metric filter and alarm exist for IAM policy changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'DeleteGroupPolicy.*DeleteRolePolicy.*DeleteUserPolicy.*PutGroupPolicy.*PutRolePolicy.*PutUserPolicy.*CreatePolicy.*DeletePolicy.*CreatePolicyVersion.*DeletePolicyVersion.*AttachRolePolicy.*DetachRolePolicy.*AttachUserPolicy.*DetachUserPolicy.*AttachGroupPolicy.*DetachGroupPolicy') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /DeletePolicy/ || /DeletePolicies/ || /Policies/ || /Policy/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for IAM policy changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*DeleteGroupPolicy.+\$\.eventName\s*=\s*DeleteRolePolicy.+\$\.eventName\s*=\s*AttachGroupPolicy.+\$\.eventName\s*=\s*DetachGroupPolicy' } diff --git a/checks/check35 b/checks/check35 index afeb522955..63ba2243cb 100644 --- a/checks/check35 +++ b/checks/check35 @@ -15,25 +15,5 @@ CHECK_TYPE_check35="LEVEL1" CHECK_ALTERNATE_check305="check35" check35(){ - # "Ensure a log metric filter and alarm exist for CloudTrail configuration changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'CreateTrail.*UpdateTrail.*DeleteTrail.*StartLogging.*StopLogging') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /TrailChange/ || /Trails/ || /CreateTrail/ || /UpdateTrail/ || /DeleteTrail/ || /StartLogging/ || /StopLogging/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for CloudTrail configuration changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*CreateTrail.+\$\.eventName\s*=\s*UpdateTrail.+\$\.eventName\s*=\s*StartLogging.+\$\.eventName\s*=\s*StopLogging' } diff --git a/checks/check36 b/checks/check36 index c57712184a..8fc6d86f5e 100644 --- a/checks/check36 +++ b/checks/check36 @@ -15,25 +15,5 @@ CHECK_TYPE_check36="LEVEL2" CHECK_ALTERNATE_check306="check36" check36(){ - # "Ensure a log metric filter and alarm exist for AWS Management Console authentication failures (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'ConsoleLogin.*Failed') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /FailedLogin/ || /ConsoleLogin/ || /Failed/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for AWS Management Console authentication failures" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventName\s*=\s*ConsoleLogin.+\$\.errorMessage\s*=\s*"Failed authentication"' } diff --git a/checks/check37 b/checks/check37 index 795b03291b..44e671135e 100644 --- a/checks/check37 +++ b/checks/check37 @@ -15,25 +15,5 @@ CHECK_TYPE_check37="LEVEL2" CHECK_ALTERNATE_check307="check37" check37(){ - # "Ensure a log metric filter and alarm exist for disabling or scheduled deletion of customer created CMKs (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'kms.amazonaws.com.*DisableKey.*ScheduleKeyDeletion') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /DisableKey/ || /ScheduleKeyDeletion/ || /kms/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for changes of customer created CMKs" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventSource\s*=\s*kms.amazonaws.com.+\$\.eventName\s*=\s*DisableKey.+\$\.eventName\s*=\s*ScheduleKeyDeletion' } diff --git a/checks/check38 b/checks/check38 index bb0ccc7a49..f018875132 100644 --- a/checks/check38 +++ b/checks/check38 @@ -15,25 +15,5 @@ CHECK_TYPE_check38="LEVEL1" CHECK_ALTERNATE_check308="check38" check38(){ - # "Ensure a log metric filter and alarm exist for S3 bucket policy changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 's3.amazonaws.com.*PutBucketAcl.*PutBucketPolicy.*PutBucketCors.*PutBucketLifecycle.*PutBucketReplication.*DeleteBucketPolicy.*DeleteBucketCors.*DeleteBucketLifecycle.*DeleteBucketReplication') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /S3/ || /BucketPolicy/ || /BucketPolicies/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for S3 bucket policy changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventSource\s*=\s*s3.amazonaws.com.+\$\.eventName\s*=\s*PutBucketAcl.+\$\.eventName\s*=\s*DeleteBucketLifecycle.+\$\.eventName\s*=\s*DeleteBucketReplication' } diff --git a/checks/check39 b/checks/check39 index 0f250a8397..d8feb0de29 100644 --- a/checks/check39 +++ b/checks/check39 @@ -15,25 +15,5 @@ CHECK_TYPE_check39="LEVEL2" CHECK_ALTERNATE_check309="check39" check39(){ - # "Ensure a log metric filter and alarm exist for AWS Config configuration changes (Scored)" - CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr ' ' ' -' | awk -F: '{ print $7 }') - if [[ $CLOUDWATCH_GROUP ]];then - for group in $CLOUDWATCH_GROUP; do - CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | awk -F: '{ print $4 }' | head -n 1) - METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name $group $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'metricFilters' | grep -E 'config.amazonaws.com.*StopConfigurationRecorder.*DeleteDeliveryChannel.*PutDeliveryChannel.*PutConfigurationRecorder') - if [[ $METRICFILTER_SET ]];then - HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region $CLOUDWATCH_LOGGROUP_REGION --query 'MetricAlarms[].MetricName' --output text | awk 'BEGIN {IGNORECASE=1}; /config/ || /ConfigurationRecorder/ || /DeliveryChannel/;') - if [[ $HAS_ALARM_ASSOCIATED ]];then - textPass "CloudWatch group $group found with metric filters and alarms for AWS Config configuration changes" - else - textFail "CloudWatch group $group found with metric filters but no alarms associated" - fi - else - textFail "CloudWatch group $group found but no metric filters or alarms associated" - fi - done - else - textFail "No CloudWatch group found for CloudTrail events" - fi + check3x '\$\.eventSource\s*=\s*config.amazonaws.com.+\$\.eventName\s*=\s*StopConfigurationRecorder.+\$\.eventName\s*=\s*PutDeliveryChannel.+\$\.eventName\s*=\s*PutConfigurationRecorder' } diff --git a/checks/check_extra713 b/checks/check_extra713 index f70c626b93..ad8cedfe4b 100644 --- a/checks/check_extra713 +++ b/checks/check_extra713 @@ -17,31 +17,26 @@ CHECK_TYPE_extra713="EXTRA" CHECK_ALTERNATE_check713="extra713" extra713(){ - - # Guardduty Regions are not the same as REGIONS - PYTHON=$(which python) - if [ ! -z "${PYTHON}" ]; then - textInfo "Using python and boto3 for getting Guardduty Available Regions" - GD_REGIONS=($($PYTHON -c "import boto3 ; session = boto3.session.Session() ; print (session.get_available_regions('guardduty'))")) - for GD_REGION in "${GD_REGIONS[@]}"; do GD_REGIONS_PARSED+="$(echo "$GD_REGION" | cut -d \' -f 2 | cut -d \' -f 1 && echo " ")"; done - else - GD_REGIONS_PARSED=$REGIONS - fi - # "Check if GuardDuty is enabled (Not Scored) (Not part of CIS benchmark)" - for regx in $GD_REGIONS_PARSED; do - LIST_OF_GUARDDUTY_DETECTORS=$($AWSCLI guardduty list-detectors $PROFILE_OPT --region $regx --output text 2> /dev/null | cut -f2) - if [[ $LIST_OF_GUARDDUTY_DETECTORS ]];then - while read -r detector;do - DETECTOR_ENABLED=$($AWSCLI guardduty get-detector --detector-id $detector $PROFILE_OPT --region $regx --query "Status" --output text|grep ENABLED) - if [[ $DETECTOR_ENABLED ]]; then - textPass "$regx: GuardDuty detector $detector enabled" "$regx" + for regx in $REGIONS; do + LIST_OF_GUARDDUTY_DETECTORS=$($AWSCLI guardduty list-detectors $PROFILE_OPT --region $regx --output text --query DetectorIds[*] 2> /dev/null) + RESULT=$? + if [ $RESULT -eq 0 ];then + if [[ $LIST_OF_GUARDDUTY_DETECTORS ]];then + while read -r detector;do + DETECTOR_ENABLED=$($AWSCLI guardduty get-detector --detector-id $detector $PROFILE_OPT --region $regx --query "Status" --output text|grep ENABLED) + if [[ $DETECTOR_ENABLED ]]; then + textPass "$regx: GuardDuty detector $detector enabled" "$regx" + else + textFail "$regx: GuardDuty detector $detector configured but suspended" "$regx" + fi + done <<< "$LIST_OF_GUARDDUTY_DETECTORS" else - textFail "$regx: GuardDuty detector $detector configured but suspended" "$regx" - fi - done <<< "$LIST_OF_GUARDDUTY_DETECTORS" - else - textFail "$regx: GuardDuty detector not configured!" "$regx" - fi + textFail "$regx: GuardDuty detector not configured!" "$regx" + fi + else + # if list-detectors return any error + textInfo "$regx: GuardDuty not checked" "$regx" + fi done -} +} \ No newline at end of file diff --git a/checks/check_extra714 b/checks/check_extra714 index 2a5233d6b3..a47ecffef5 100644 --- a/checks/check_extra714 +++ b/checks/check_extra714 @@ -18,19 +18,17 @@ CHECK_ALTERNATE_check714="extra714" extra714(){ # "Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark)" - for regx in $REGIONS; do - LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --region $regx --query 'DistributionList.Items[].Id' --output text |grep -v "^None") - if [[ $LIST_OF_DISTRIBUTIONS ]]; then - for cdn in $LIST_OF_DISTRIBUTIONS;do - CDN_LOG_ENABLED=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --region $regx --id "$cdn" --query 'Distribution.DistributionConfig.Logging.Enabled' | grep true) - if [[ $CDN_LOG_ENABLED ]];then - textPass "$regx: CDN $cdn logging enabled" "$regx" - else - textFail "$regx: CDN $cdn logging disabled!" "$regx" - fi - done - else - textInfo "$regx: No CDN configured" "$regx" - fi - done + LIST_OF_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[].Id' --output text | grep -v "^None") + if [[ $LIST_OF_DISTRIBUTIONS ]]; then + for dist in $LIST_OF_DISTRIBUTIONS; do + LOG_ENABLED=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --id "$dist" --query 'Distribution.DistributionConfig.Logging.Enabled' | grep true) + if [[ $LOG_ENABLED ]]; then + textPass "CloudFront distribution $dist has logging enabled" + else + textFail "CloudFront distribution $dist has logging disabled" + fi + done + else + textInfo "No CloudFront distributions found" + fi } diff --git a/checks/check_extra720 b/checks/check_extra720 index 9acff44100..1bf63cc59f 100644 --- a/checks/check_extra720 +++ b/checks/check_extra720 @@ -25,7 +25,7 @@ extra720(){ LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query trailList[?HomeRegion==\`$regx\`].Name --output text) if [[ $LIST_OF_TRAILS ]]; then for trail in $LIST_OF_TRAILS; do - FUNCTION_ENABLED_IN_TRAIL=$($AWSCLI cloudtrail get-event-selectors $PROFILE_OPT --trail-name $trail --region $regx --query "EventSelectors[*].DataResources[?Type == \`AWS::Lambda::Function\`].Values" --output text |xargs -n1| grep -E "^arn:aws:lambda.*function:$lambdafunction$") + FUNCTION_ENABLED_IN_TRAIL=$($AWSCLI cloudtrail get-event-selectors $PROFILE_OPT --trail-name $trail --region $regx --query "EventSelectors[*].DataResources[?Type == \`AWS::Lambda::Function\`].Values" --output text |xargs -n1| grep -E "^arn:aws:lambda.*function:$lambdafunction$|^arn:aws:lambda$") if [[ $FUNCTION_ENABLED_IN_TRAIL ]]; then textPass "$regx: Lambda function $lambdafunction enabled in trail $trail" "$regx" else diff --git a/checks/check_extra727 b/checks/check_extra727 index cca42d5c68..0be47f2c25 100644 --- a/checks/check_extra727 +++ b/checks/check_extra727 @@ -25,14 +25,14 @@ extra727(){ # check if the policy has Principal as * SQS_TO_CHECK=$($AWSCLI sqs get-queue-attributes --queue-url $queue $PROFILE_OPT --region $regx --attribute-names All --query Attributes.Policy --output text | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | awk '/Principal/ || /Condition/ && !skip { print } { skip = /Deny/} ') PUBLIC_SQS_WCONDITION=$(echo $SQS_TO_CHECK|grep Condition) - if [[ $PUBLIC_SQS_WCONDITION ]]; then - textInfo "$regx: SQS queue $queue has a Condition" "$regx" + if [[ $PUBLIC_SQS_WCONDITION ]]; then + textPass "$regx: SQS queue $queue has a Condition" "$regx" else PUBLIC_SQS=$(echo $SQS_TO_CHECK|grep \"Principal|grep \*) if [[ $PUBLIC_SQS ]]; then textFail "$regx: SQS queue $queue seems to be public (Principal: \"*\")" "$regx" else - textInfo "$regx: SQS queue $queue seems correct" "$regx" + textPass "$regx: SQS queue $queue seems correct" "$regx" fi fi done diff --git a/checks/check_extra73 b/checks/check_extra73 index 216d198f47..7ca96c7f77 100644 --- a/checks/check_extra73 +++ b/checks/check_extra73 @@ -34,9 +34,9 @@ CHECK_ALTERNATE_check703="extra73" # # BUCKET_POLICY_STATUS=$($AWSCLI s3api get-bucket-policy-status --bucket $bucket --query PolicyStatus.IsPublic --output text | grep False) # if [[ $BUCKET_POLICY_STATUS ]];then -# textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$regx" +# textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION" # else -# textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$regx" +# textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION" # fi # done # } @@ -44,40 +44,46 @@ CHECK_ALTERNATE_check703="extra73" extra73(){ textInfo "Looking for open S3 Buckets (ACLs and Policies) in all regions... " - ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' $PROFILE_OPT --region $REGION --output text) + ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' $PROFILE_OPT --output text) for bucket in $ALL_BUCKETS_LIST; do - BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket $PROFILE_OPT --region $REGION --output text) + BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket $PROFILE_OPT --output text) if [[ "None" == $BUCKET_LOCATION ]]; then BUCKET_LOCATION="us-east-1" fi if [[ "EU" == $BUCKET_LOCATION ]]; then BUCKET_LOCATION="eu-west-1" fi - # check if AllUsers is in the ACL as Grantee - CHECK_BUCKET_ALLUSERS_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AllUsers']" --output text |grep -v GRANTEE) - CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_ALLUSERS_ACL) - # check if AuthenticatedUsers is in the ACL as Grantee, they will have access with sigened URL only - CHECK_BUCKET_AUTHUSERS_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers']" --output text |grep -v GRANTEE) - CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_AUTHUSERS_ACL) - # to prevent error NoSuchBucketPolicy first clean the output controlling stderr - TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-${bucket}.policy.XXXXXXXXXX) - $AWSCLI s3api get-bucket-policy $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output text --query Policy > $TEMP_POLICY_FILE 2> /dev/null - # check if the S3 policy has Principal as * - CHECK_BUCKET_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | sed -e 's/[{}]/''/g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'|awk '/Principal/ && !skip { print } { skip = /Deny/} '|grep ^\"Principal|grep \*) - if [[ $CHECK_BUCKET_ALLUSERS_ACL || $CHECK_BUCKET_AUTHUSERS_ACL || $CHECK_BUCKET_ALLUSERS_POLICY ]];then - if [[ $CHECK_BUCKET_ALLUSERS_ACL ]];then - textFail "$BUCKET_LOCATION: $bucket bucket is open to the Internet (Everyone) with permissions: $CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE" "$regx" - fi - if [[ $CHECK_BUCKET_AUTHUSERS_ACL ]];then - textFail "$BUCKET_LOCATION: $bucket bucket is open to Authenticated users (Any AWS user) with permissions: $CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE" "$regx" - fi - if [[ $CHECK_BUCKET_ALLUSERS_POLICY ]];then - textFail "$BUCKET_LOCATION: $bucket bucket policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx" - fi + # Check Explicit Deny and Avoid Error + CHEK_FOR_EXPLICIT_DENY=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output text 2>&1) + if [[ $(echo "$CHEK_FOR_EXPLICIT_DENY" | grep AccessDenied) ]] ; then + textPass "$BUCKET_LOCATION: bucket have an explicit Deny. Not possible to get ACL." "$BUCKET_LOCATION" else - textPass "$BUCKET_LOCATION: $bucket bucket is not open" "$regx" + # check if AllUsers is in the ACL as Grantee + CHECK_BUCKET_ALLUSERS_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AllUsers']" --output text |grep -v GRANTEE) + CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_ALLUSERS_ACL) + # check if AuthenticatedUsers is in the ACL as Grantee, they will have access with sigened URL only + CHECK_BUCKET_AUTHUSERS_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query "Grants[?Grantee.URI == 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers']" --output text |grep -v GRANTEE) + CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE=$(echo -ne $CHECK_BUCKET_AUTHUSERS_ACL) + # to prevent error NoSuchBucketPolicy first clean the output controlling stderr + TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-${bucket}.policy.XXXXXXXXXX) + $AWSCLI s3api get-bucket-policy $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output text --query Policy > $TEMP_POLICY_FILE 2> /dev/null + # check if the S3 policy has Principal as * + CHECK_BUCKET_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | sed -e 's/[{}]/''/g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'|awk '/Principal/ && !skip { print } { skip = /Deny/} '|grep ^\"Principal|grep \*) + if [[ $CHECK_BUCKET_ALLUSERS_ACL || $CHECK_BUCKET_AUTHUSERS_ACL || $CHECK_BUCKET_ALLUSERS_POLICY ]];then + if [[ $CHECK_BUCKET_ALLUSERS_ACL ]];then + textFail "$BUCKET_LOCATION: $bucket bucket is open to the Internet (Everyone) with permissions: $CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE" "$BUCKET_LOCATION" + fi + if [[ $CHECK_BUCKET_AUTHUSERS_ACL ]];then + textFail "$BUCKET_LOCATION: $bucket bucket is open to Authenticated users (Any AWS user) with permissions: $CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE" "$BUCKET_LOCATION" + fi + if [[ $CHECK_BUCKET_ALLUSERS_POLICY ]];then + textFail "$BUCKET_LOCATION: $bucket bucket policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$BUCKET_LOCATION" + fi + else + textPass "$BUCKET_LOCATION: $bucket bucket is not open" "$BUCKET_LOCATION" + fi + rm -fr $TEMP_POLICY_FILE fi - rm -fr $TEMP_POLICY_FILE done } @@ -115,16 +121,16 @@ extra73(){ # CHECK_BUCKET_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | sed -e 's/[{}]/''/g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'|awk '/Principal/ && !skip { print } { skip = /Deny/} '|grep ^\"Principal|grep \*) # if [[ $CHECK_BUCKET_ALLUSERS_ACL || $CHECK_BUCKET_AUTHUSERS_ACL || $CHECK_BUCKET_ALLUSERS_POLICY ]];then # if [[ $CHECK_BUCKET_ALLUSERS_ACL ]];then -# textWarn "$BUCKET_LOCATION: $bucket bucket is open to the Internet (Everyone) with permissions: $CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE" "$regx" +# textWarn "$BUCKET_LOCATION: $bucket bucket is open to the Internet (Everyone) with permissions: $CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE" "$BUCKET_LOCATION" # fi # if [[ $CHECK_BUCKET_AUTHUSERS_ACL ]];then -# textWarn "$BUCKET_LOCATION: $bucket bucket is open to Authenticated users (Any AWS user) with permissions: $CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE" "$regx" +# textWarn "$BUCKET_LOCATION: $bucket bucket is open to Authenticated users (Any AWS user) with permissions: $CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE" "$BUCKET_LOCATION" # fi # if [[ $CHECK_BUCKET_ALLUSERS_POLICY ]];then -# textWarn "$BUCKET_LOCATION: $bucket bucket policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx" +# textWarn "$BUCKET_LOCATION: $bucket bucket policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$BUCKET_LOCATION" # fi # else -# textOK "$BUCKET_LOCATION: $bucket bucket is not open" "$regx" +# textOK "$BUCKET_LOCATION: $bucket bucket is not open" "$BUCKET_LOCATION" # fi # rm -fr $TEMP_POLICY_FILE # } diff --git a/checks/check_extra734 b/checks/check_extra734 index dae7869ad4..63e9aab65a 100644 --- a/checks/check_extra734 +++ b/checks/check_extra734 @@ -21,34 +21,33 @@ extra734(){ if [[ $LIST_OF_BUCKETS ]]; then for bucket in $LIST_OF_BUCKETS;do # query to get if has encryption enabled or not - RESULT=$(echo $bucket $($AWSCLI s3api get-bucket-encryption $PROFILE_OPT --bucket $bucket --query ServerSideEncryptionConfiguration.Rules[].ApplyServerSideEncryptionByDefault[].SSEAlgorithm --output text 2>&1 | grep -v ServerSideEncryptionConfigurationNotFoundError)) - TEMP_SSE_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-${bucket}.policy.XXXXXXXXXX) - # get bucket policy - $AWSCLI s3api get-bucket-policy $PROFILE_OPT --bucket $bucket --output text --query Policy > $TEMP_SSE_POLICY_FILE 2> /dev/null - # check if the S3 policy forces SSE s3:x-amz-server-side-encryption:true - CHECK_BUCKET_SSE_POLICY_PRESENT=$(cat $TEMP_SSE_POLICY_FILE | sed -e 's/[{}]/''/g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'| awk '/Condition/ && !skip { print } { skip = /x-amz-server-side-encryption/} '|grep \"true\") - CHECK_BUCKET_SSE_POLICY_VALUE=$(cat $TEMP_SSE_POLICY_FILE | sed -e 's/[{}]/''/g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'| awk '/Condition/ && !skip { print } { skip = /x-amz-server-side-encryption/} '|grep -Eo "AES256|aws:kms") - ENCRYPTION=false + RESULT=$($AWSCLI s3api get-bucket-encryption $PROFILE_OPT --bucket $bucket --query ServerSideEncryptionConfiguration.Rules[].ApplyServerSideEncryptionByDefault[].SSEAlgorithm --output text 2>&1) + if [[ $(echo "$RESULT" | grep ServerSideEncryptionConfigurationNotFoundError) ]] ; then + textFail "Bucket $bucket does not enforce encryption!" + elif [[ $(echo "$RESULT" | grep AccessDenied) ]] ; then + textFail "Access Denied Trying to Get Encryption for $bucket" + else + TEMP_SSE_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-${bucket}.policy.XXXXXXXXXX) + # get bucket policy + $AWSCLI s3api get-bucket-policy $PROFILE_OPT --bucket $bucket --output text --query Policy > $TEMP_SSE_POLICY_FILE 2> /dev/null + # check if the S3 policy forces SSE s3:x-amz-server-side-encryption:true + CHECK_BUCKET_SSE_POLICY_PRESENT=$(cat $TEMP_SSE_POLICY_FILE | sed -e 's/[{}]/''/g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'| awk '/Condition/ && !skip { print } { skip = /x-amz-server-side-encryption/} '|grep \"true\") + CHECK_BUCKET_SSE_POLICY_VALUE=$(cat $TEMP_SSE_POLICY_FILE | sed -e 's/[{}]/''/g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'| awk '/Condition/ && !skip { print } { skip = /x-amz-server-side-encryption/} '|grep -Eo "AES256|aws:kms") - echo "$RESULT" | while read RBUCKET SSEALG; do - if [[ $SSEALG ]]; then - textPass "Bucket $RBUCKET has default encryption enabled with algorithm $SSEALG" - ENCRYPTION=true + echo "$RESULT" | while read RBUCKET SSEALG; do + if [[ $SSEALG ]]; then + textPass "Bucket $RBUCKET has default encryption enabled with algorithm $SSEALG" + fi + done + if [[ $CHECK_BUCKET_SSE_POLICY_PRESENT && $CHECK_BUCKET_SSE_POLICY_VALUE ]]; then + textPass "Bucket $bucket has S3 bucket policy to enforce encryption with $CHECK_BUCKET_SSE_POLICY_VALUE" fi - done - if [[ $CHECK_BUCKET_SSE_POLICY_PRESENT && $CHECK_BUCKET_SSE_POLICY_VALUE ]]; then - textPass "Bucket $bucket has S3 bucket policy to enforce encryption with $CHECK_BUCKET_SSE_POLICY_VALUE" - ENCRYPTION=true - fi - - if [ "$ENCRYPTION" == false ]; then - textFail "Bucket $bucket does not enforce encryption!" - fi + rm -fr $TEMP_SSE_POLICY_FILE + fi done - rm -fr $TEMP_SSE_POLICY_FILE + else textInfo "No S3 Buckets found" fi } - diff --git a/iam/prowler-policy-additions.json b/iam/prowler-policy-additions.json index de9bfd9621..7b2f2f7999 100644 --- a/iam/prowler-policy-additions.json +++ b/iam/prowler-policy-additions.json @@ -11,6 +11,8 @@ "ses:getidentityverificationattributes", "sns:listsubscriptionsbytopic", "guardduty:ListDetectors", + "guardduty:GetDetector", + "S3:GetEncryptionConfiguration", "trustedadvisor:Describe*", "cloudtrail:GetEventSelectors", "apigateway:GET", diff --git a/include/check3x b/include/check3x new file mode 100644 index 0000000000..70bf9b910a --- /dev/null +++ b/include/check3x @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# Prowler - the handy cloud security tool (c) by Toni de la Fuente +# +# This Prowler check is licensed under a +# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. +# +# You should have received a copy of the license along with this +# work. If not, see . + +check3x(){ + grep_filter=$1 + local CHECK_OK + local CHECK_WARN + local CHECK_CROSS_ACCOUNT_WARN + + CLOUDWATCH_GROUP=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text| tr '\011' '\012' | awk -F: '{print $7}') + CURRENT_ACCOUNT_ID=$($AWSCLI sts $PROFILE_OPT get-caller-identity --region "$REGION" --query Account --output text) + + if [[ $CLOUDWATCH_GROUP ]];then + for group in $CLOUDWATCH_GROUP; do + CLOUDWATCH_LOGGROUP_REGION=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | grep "$group" | awk -F: '{ print $4 }' | head -n 1) + CLOUDWATCH_ACCOUNT_ID=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].CloudWatchLogsLogGroupArn' --output text | tr '\011' '\012' | grep "$group" | awk -F: '{ print $5 }' | head -n 1) + if [ "$CLOUDWATCH_ACCOUNT_ID" == "$CURRENT_ACCOUNT_ID" ];then + METRICFILTER_SET=$($AWSCLI logs describe-metric-filters --log-group-name "$group" $PROFILE_OPT --region "$CLOUDWATCH_LOGGROUP_REGION" --output text | grep METRICFILTERS | grep -E "$grep_filter" | awk '{ print $3 }') + fi + if [[ $METRICFILTER_SET ]];then + for metric in $METRICFILTER_SET; do + metric_name=$($AWSCLI logs describe-metric-filters $PROFILE_OPT --region "$CLOUDWATCH_LOGGROUP_REGION" --log-group-name "$group" --filter-name-prefix "$metric" --output text --query 'metricFilters[0].metricTransformations[0].metricName') + HAS_ALARM_ASSOCIATED=$($AWSCLI cloudwatch describe-alarms $PROFILE_OPT --region "$CLOUDWATCH_LOGGROUP_REGION" --query 'MetricAlarms[?MetricName==`'"$metric_name"'`]' --output text) + if [[ $HAS_ALARM_ASSOCIATED ]];then + CHECK_OK="$CHECK_OK $group:$metric" + else + CHECK_WARN="$CHECK_WARN $group:$metric" + fi + done + elif [ "$CLOUDWATCH_ACCOUNT_ID" == "$CURRENT_ACCOUNT_ID" ];then + CHECK_WARN="$CHECK_WARN $group" + else + CHECK_CROSS_ACCOUNT_WARN="$CHECK_CROSS_ACCOUNT_WARN $group" + fi + done + + if [[ $CHECK_OK ]]; then + for group in $CHECK_OK; do + metric=${group#*:} + group=${group%:*} + textPass "CloudWatch group $group found with metric filter $metric and alarms set" + done + fi + if [[ $CHECK_WARN ]]; then + for group in $CHECK_WARN; do + case $group in + *:*) metric=${group#*:} + group=${group%:*} + textFail "CloudWatch group $group found with metric filter $metric but no alarms associated" + ;; + *) textFail "CloudWatch group $group found but no metric filters or alarms associated" + esac + done + fi + if [[ $CHECK_CROSS_ACCOUNT_WARN ]]; then + for group in $CHECK_CROSS_ACCOUNT_WARN; do + textInfo "CloudWatch group $group is not in this account" + done + fi + else + textFail "No CloudWatch group found for CloudTrail events" + fi +} diff --git a/include/outputs b/include/outputs index a343a4ae4d..6e379d0f82 100644 --- a/include/outputs +++ b/include/outputs @@ -27,7 +27,7 @@ textPass(){ else REPREGION=$REGION fi - jq -c \ + jq -M -c \ --arg PROFILE "$PROFILE" \ --arg ACCOUNT_NUM "$ACCOUNT_NUM" \ --arg TITLE_TEXT "$TITLE_TEXT" \ diff --git a/prowler b/prowler index c74f2db898..7f92e7b1e2 100755 --- a/prowler +++ b/prowler @@ -165,6 +165,7 @@ done . $PROWLER_DIR/include/scoring . $PROWLER_DIR/include/python_detector . $PROWLER_DIR/include/secrets_detector +. $PROWLER_DIR/include/check3x # Get a list of all available AWS Regions REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' \ @@ -241,12 +242,27 @@ execute_check() { # Function to execute all checks in a group execute_group() { - show_group_title $1 - # run the checks in the group - IFS=',' read -ra CHECKS <<< ${GROUP_CHECKS[$1]} - for i in ${CHECKS[@]}; do - execute_check $i - done + show_group_title $1 + # run the checks in the group + IFS=',' read -ra CHECKS <<< ${GROUP_CHECKS[$1]} + + # Exclude any checks specified + if [[ -n ${2} ]]; then + EXCLUDED_CHECKS=() + NEW_CHECKS=() + IFS=',' read -ra EXCLUDED_CHECKS <<< "${2}," + for exc in ${EXCLUDED_CHECKS[@]} ; do + for i in ${CHECKS[@]} ; do + [[ ${i} != ${exc} ]] && NEW_CHECKS+=(${i}) + done + done + CHECKS=("${NEW_CHECKS[@]}") + unset NEW_CHECKS + unset EXCLUDED_CHECKS + fi + for i in ${CHECKS[@]}; do + execute_check ${i} + done } # Function to execute group by name @@ -257,14 +273,17 @@ execute_group_by_id() { fi for i in "${!GROUP_ID[@]}"; do if [ "${GROUP_ID[$i]}" == "$1" ]; then - execute_group $i + execute_group ${i} $2 fi done } -# Function to execute all checks in all groups +# Function to execute all checks in all groups except extras if -e is invoked execute_all() { for i in "${!GROUP_TITLE[@]}"; do + if [[ $EXTRAS ]]; then + GROUP_RUN_BY_DEFAULT[7]='N' + fi if [ "${GROUP_RUN_BY_DEFAULT[$i]}" == "Y" ]; then execute_group $i fi @@ -350,14 +369,31 @@ fi # Gather account data / test aws cli connectivity getWhoami +# Execute group of checks if called with -g +if [[ $GROUP_ID_READ ]];then + if [[ " ${GROUP_ID[@]} " =~ " ${GROUP_ID_READ} " ]]; then + if [[ $MODE == "csv" ]]; then + BANNER=0 + fi + execute_group_by_id ${GROUP_ID_READ} ${EXCLUDE_CHECK_ID} + cleanTemp + scoring + exit $EXITCODE + else + textFail "Use a valid check group ID i.e.: group1, extras, forensics-ready, etc." + show_all_group_titles + exit $EXITCODE + fi +fi + # Get a list of total checks excluding a list provided by the user and overwrite CHECK_ID with the result # if the list provided by the user contains an invalid check, this will be discarded. # if the list provided by the user contains just one argument and is invalid, then it will be discarded and all tests will be executed -if [[ $EXCLUDE_CHECK_ID ]];then - get_all_checks_without_exclusion $EXCLUDE_CHECK_ID +if [[ ${EXCLUDE_CHECK_ID} ]];then + get_all_checks_without_exclusion ${EXCLUDE_CHECK_ID} function join { local IFS="$1"; shift; echo "$*"; } CHECKS_EXCLUDED=$(join , "${CHECKS_EXCLUDED[@]}") - CHECK_ID=$CHECKS_EXCLUDED + CHECK_ID=${CHECKS_EXCLUDED} fi # Execute single check if called with -c @@ -370,22 +406,6 @@ if [[ $CHECK_ID ]];then exit $EXITCODE fi -# Execute group of checks if called with -g -if [[ $GROUP_ID_READ ]];then - if [[ " ${GROUP_ID[@]} " =~ " ${GROUP_ID_READ} " ]]; then - if [[ $MODE == "csv" ]]; then - BANNER=0 - fi - execute_group_by_id $GROUP_ID_READ - cleanTemp - scoring - exit $EXITCODE - else - textFail "Use a valid check group ID i.e.: group1, extras, forensics-ready, etc." - show_all_group_titles - exit $EXITCODE - fi -fi execute_all scoring