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