Merge pull request #2 from toniblyx/master

Update from master repo
This commit is contained in:
Nimrod Kor authored and GitHub committed 2019-10-08 08:46:02 +03:00
commit 237dcd885f
29 files changed
+276 -473

No files matched your search

+3 -3
View File
@@ -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"
+11 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+2
View File
@@ -11,6 +11,8 @@
"ses:getidentityverificationattributes",
"sns:listsubscriptionsbytopic",
"guardduty:ListDetectors",
"guardduty:GetDetector",
"S3:GetEncryptionConfiguration",
"trustedadvisor:Describe*",
"cloudtrail:GetEventSelectors",
"apigateway:GET",
+70
View File
@@ -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
View File
@@ -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" \
+47 -27
View File
@@ -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