mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-23 20:04:27 +00:00
29 files changed
+276
-473
No files matched your search
@@ -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"
|
||||
@@ -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 <https://aws.amazon.com/cli/>
|
||||
AWS-CLI can be also installed it using "brew", "apt", "yum" or manually from <https://aws.amazon.com/cli/>, 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 <YOUR_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
|
||||
|
||||
|
||||
+3
-3
@@ -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"
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+28
-11
@@ -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
|
||||
}
|
||||
+1
-45
@@ -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\*"'
|
||||
}
|
||||
+1
-22
@@ -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'
|
||||
}
|
||||
+1
-22
@@ -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'
|
||||
}
|
||||
+1
-22
@@ -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'
|
||||
}
|
||||
+1
-22
@@ -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'
|
||||
}
|
||||
+1
-22
@@ -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'
|
||||
}
|
||||
+1
-21
@@ -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"'
|
||||
}
|
||||
+1
-21
@@ -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"'
|
||||
}
|
||||
+1
-21
@@ -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'
|
||||
}
|
||||
+1
-21
@@ -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'
|
||||
}
|
||||
+1
-21
@@ -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"'
|
||||
}
|
||||
+1
-21
@@ -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'
|
||||
}
|
||||
+1
-21
@@ -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'
|
||||
}
|
||||
+1
-21
@@ -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'
|
||||
}
|
||||
+20
-25
@@ -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
|
||||
}
|
||||
}
|
||||
+13
-15
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+37
-31
@@ -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
|
||||
# }
|
||||
+22
-23
@@ -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
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
"ses:getidentityverificationattributes",
|
||||
"sns:listsubscriptionsbytopic",
|
||||
"guardduty:ListDetectors",
|
||||
"guardduty:GetDetector",
|
||||
"S3:GetEncryptionConfiguration",
|
||||
"trustedadvisor:Describe*",
|
||||
"cloudtrail:GetEventSelectors",
|
||||
"apigateway:GET",
|
||||
|
||||
@@ -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 <http://creativecommons.org/licenses/by-nc-sa/4.0/>.
|
||||
|
||||
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
|
||||
}
|
||||
+1
-1
@@ -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" \
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user