Merge pull request #6 from bridgecrewio/merge_from_tony

Merge from tony
This commit is contained in:
Nimrod Kor authored and GitHub committed 2020-02-02 10:32:39 +02:00
commit 2f26dacb90
27 files changed
+463 -317

No files matched your search

-14
View File
@@ -1,14 +0,0 @@
FROM toniblyx/prowler
MAINTAINER Bridgecrew <www.bridgecrew.io>
USER root
WORKDIR "./prowler"
COPY "checks" "checks"
COPY "./run.sh" "."
RUN chown -R prowler /prowler/
USER prowler
ENTRYPOINT ["./run.sh"]
+49 -2
View File
@@ -6,6 +6,7 @@
- [Features](#features)
- [Requirements and Installation](#requirements-and-installation)
- [Usage](#usage)
- [Advanced Usage](#advanced-usage)
- [Fix](#fix)
- [Screenshots](#screenshots)
- [Troubleshooting](#troubleshooting)
@@ -63,6 +64,11 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
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`. You will need to install `jq` to get more accuracy in some checks.
- Make sure jq is installed (example below with "apt" but use a valid package manager for your OS):
```sh
sudo apt install jq
```
- Previous steps, from your workstation:
```sh
@@ -70,7 +76,7 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
cd prowler
```
- Make sure you have properly configured your AWS-CLI with a valid Access Key and Region or declare AWS variables properly:
- Make sure you have properly configured your AWS-CLI with a valid Access Key and Region or declare AWS variables properly (or intance profile):
```sh
aws configure
@@ -88,7 +94,7 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
arn:aws:iam::aws:policy/SecurityAudit
```
> In some cases you may need more list or get permissions in some services, look at the Troubleshooting section for a more comprehensive policy if you find issues with the default SecurityAudit policy.
> Additional permissions needed: to make sure Prowler can scan all services included in the group *Extras*, make sure you attach also the custom policy [prowler-additions-policy.json](https://github.com/toniblyx/prowler/blob/master/iam/prowler-additions-policy.json) to the role you are using.
## Usage
@@ -222,9 +228,50 @@ This script has been written in bash using AWS-CLI and it works in Linux and OSX
-b do not print Prowler banner
-V show version number & exit
-s show scoring report
-x specify external directory with custom checks (i.e. /my/own/checks, files must start by check)
-q suppress info messages and passing test output
-A account id for the account where to assume a role, requires -R and -T
(i.e.: 123456789012)
-R role name to assume in the account, requires -A and -T
(i.e.: ProwlerRole)
-T session durantion given to that role credentials in seconds, default 1h (3600) recommended 12h, requires -R and -T
(i.e.: 43200)
-h this help
```
## Advanced Usage
### Assume Role:
Prowler uses the AWS CLI underneath so it uses the same authentication methods. However, there are few ways to run Prowler against multiple accounts using IAM Assume Role feature depending on eachg use case. You can just set up your custom profile inside `~/.aws/config` with all needed information about the role to assume then call it with `./prowler -p your-custom-profile`. Additionally you can use `-A 123456789012` and `-R RemoteRoleToAssume` and Prowler will get those temporary credentials using `aws sts assume-role`, set them up as environment variables and run against that given account.
```
./prowler -A 123456789012 -R ProwlerRole
```
> *NOTE 1 about Session Duration*: By default it gets credentials valid for 1 hour (3600 seconds). Depending on the mount of checks you run and the size of your infrastructure, Prowler may require more than 1 hour to finish. Use option `-T <seconds>` to allow up to 12h (43200 seconds). To allow more than 1h you need to modify *"Maximum CLI/API session duration"* for that particular role, read more [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session).
> *NOTE 2 about Session Duration*: Bear in mind that if you are using roles assumed by role chaining there is a hard limit of 1 hour so consider not using role chaining if possible, read more about that, in foot note 1 below the table [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html).
For example, if you want to get only the fails in CSV format from all checks regarding RDS without banner from the AWS Account 123456789012 assuming the role RemoteRoleToAssume and set a fixed session duration of 1h:
```
./prowler -A 123456789012 -R RemoteRoleToAssume -T 3600 -b -M cvs -q -g rds
```
### Custom folder for custom checks
Flag `-x /my/own/checks` will include any check in that particular directory. To see how to write checks see [Add Custom Checks](#add-custom-checks) section.
### Show or log only FAILs
In order to remove noise and get only FAIL findings there is a `-q` flag that makes Prowler to show and log only FAILs. It can be combined with any other option.
```
./prowler -q -M csv -b
```
## How to fix every FAIL
Check your report and fix the issues following all specific guidelines per check in <https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf>
+7 -3
View File
@@ -17,10 +17,14 @@ CHECK_ALTERNATE_check203="check23"
check23(){
# "Ensure the S3 bucket CloudTrail logs to is not publicly accessible (Scored)"
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails --query 'trailList[*].S3BucketName' --output text $PROFILE_OPT --region $REGION)
if [[ $CLOUDTRAILBUCKET ]];then
if [[ $CLOUDTRAILBUCKET ]]; then
for bucket in $CLOUDTRAILBUCKET;do
CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $bucket --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' $PROFILE_OPT --region $REGION --output text)
if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]];then
CLOUDTRAILBUCKET_HASALLPERMISIONS=$($AWSCLI s3api get-bucket-acl --bucket $bucket --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' $PROFILE_OPT --region $REGION --output text 2>&1)
if [[ $(echo "$CLOUDTRAILBUCKET_HASALLPERMISIONS" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to Get Bucket Acl for $bucket"
continue
fi
if [[ $CLOUDTRAILBUCKET_HASALLPERMISIONS ]]; then
textFail "check your $bucket CloudTrail bucket ACL and Policy!"
else
textPass "Bucket $bucket is set correctly"
+9 -6
View File
@@ -20,16 +20,20 @@ check26(){
CLOUDTRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].Name' --output text| tr '\011' '\012' | awk -F: '{print $1}')
ACCOUNT_ID=$($AWSCLI sts get-caller-identity --output json $PROFILE_OPT --region $REGION --query "Account" | tr -d '"')
if [[ $CLOUDTRAILS ]];then
if [[ $CLOUDTRAILS ]]; then
for trail in $CLOUDTRAILS; do
CLOUDTRAIL_ACCOUNT_ID=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region "$REGION" --query 'trailList[*].TrailARN' --output text | tr '\011' '\012' | grep "$trail" | awk -F: '{ print $5 }' | head -n 1)
CLOUDTRAILBUCKET=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $REGION --query 'trailList[*].[Name, S3BucketName]' --output text | tr '\011' ':' | grep "$trail" | awk -F: '{ print $2 }' )
if [[ $CLOUDTRAILBUCKET ]];then
if [[ $CLOUDTRAILBUCKET ]]; then
bucket=$CLOUDTRAILBUCKET
if [ "$CLOUDTRAIL_ACCOUNT_ID" == "$ACCOUNT_NUM" ];then
CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --region $REGION --query 'LoggingEnabled.TargetBucket' --output text|grep -v None)
if [[ $CLOUDTRAILBUCKET_LOGENABLED ]];then
if [ "$CLOUDTRAIL_ACCOUNT_ID" == "$ACCOUNT_NUM" ]; then
CLOUDTRAILBUCKET_LOGENABLED=$($AWSCLI s3api get-bucket-logging --bucket $bucket $PROFILE_OPT --region $REGION --query 'LoggingEnabled.TargetBucket' --output text 2>&1)
if [[ $(echo "$CLOUDTRAILBUCKET_LOGENABLED" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to Get Bucket Logging for $bucket"
continue
fi
if [[ $CLOUDTRAILBUCKET_LOGENABLED != "null" ]]; then
textPass "Bucket access logging enabled in CloudTrail S3 bucket $bucket for $trail"
else
textFail "Bucket access logging is not enabled in CloudTrail S3 bucket $bucket for $trail"
@@ -37,7 +41,6 @@ check26(){
else
textInfo "CloudTrail S3 bucket $bucket for trail $trail is not in current account"
fi
else
textFail "CloudTrail bucket not found!"
fi
+1 -1
View File
@@ -36,10 +36,10 @@ extra716(){
else
textPass "$regx: $domain is in a VPC" "$regx"
fi
rm -f $TEMP_POLICY_FILE
done
else
textInfo "$regx: No Elasticsearch Service domain found" "$regx"
fi
rm -fr $TEMP_POLICY_FILE
done
}
+1 -1
View File
@@ -11,7 +11,7 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra723="7.23"
CHECK_TITLE_extra723="[extra723] Check if RDS Snapshots are public (Not Scored) (Not part of CIS benchmark)"
CHECK_TITLE_extra723="[extra723] Check if RDS Snapshots and Cluster Snapshots are public (Not Scored) (Not part of CIS benchmark)"
CHECK_SCORED_extra723="NOT_SCORED"
CHECK_TYPE_extra723="EXTRA"
CHECK_ALTERNATE_check723="extra723"
+18 -7
View File
@@ -29,12 +29,23 @@ extra726(){
for checkid in $TA_CHECKS_ID; do
TA_CHECKS_NAME=$($AWSCLI support describe-trusted-advisor-checks --language en $PROFILE_OPT --region us-east-1 --query "checks[?id==\`$checkid\`].{name:name}[*]" --output text)
QUERY_TA_CHECK_RESULT=$($AWSCLI support describe-trusted-advisor-check-result --check-id $checkid --language en $PROFILE_OPT --region us-east-1 --query 'result.status' --output text)
if [[ $(echo $QUERY_TA_CHECK_RESULT | grep ok) ]]; then
textPass "Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT"
elif [[ $(echo $QUERY_TA_CHECK_RESULT | grep warning) ]]; then
textInfo "Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT"
else
textFail "Trusted Advisor check $TA_CHECKS_NAME is in state $QUERY_TA_CHECK_RESULT"
fi
# Possible results - https://docs.aws.amazon.com/cli/latest/reference/support/describe-trusted-advisor-check-result.html
case "$QUERY_TA_CHECK_RESULT" in
"ok")
textPass "Trusted Advisor check $TA_CHECKS_NAME is in ok state $QUERY_TA_CHECK_RESULT"
;;
"error")
textFail "Trusted Advisor check $TA_CHECKS_NAME is in error state $QUERY_TA_CHECK_RESULT"
;;
"warning")
textInfo "Trusted Advisor check $TA_CHECKS_NAME is in warning state $QUERY_TA_CHECK_RESULT"
;;
"not_available")
textInfo "Trusted Advisor check $TA_CHECKS_NAME is in not_available state $QUERY_TA_CHECK_RESULT"
;;
"*")
textFail "Trusted Advisor check $TA_CHECKS_NAME is in unknown state $QUERY_TA_CHECK_RESULT"
;;
esac
done
}
+110 -136
View File
@@ -10,6 +10,7 @@
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra73="7.3,7.03"
CHECK_TITLE_extra73="[extra73] Ensure there are no S3 buckets open to the Everyone or Any AWS user (Not Scored) (Not part of CIS benchmark)"
CHECK_SCORED_extra73="NOT_SCORED"
@@ -18,46 +19,65 @@ CHECK_ALTERNATE_extra703="extra73"
CHECK_ALTERNATE_check73="extra73"
CHECK_ALTERNATE_check703="extra73"
# Improved and simplified check on Nov 18th 2018 due to a new bucket attribute
# called PolicyStatus, not available in all regions yet.
# extra73(){
# ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' $PROFILE_OPT --region $REGION --output text)
# for bucket in $ALL_BUCKETS_LIST; do
# BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket $PROFILE_OPT --region $REGION --output text 2>&1)
# if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
# textFail "Access Denied Trying to Get Bucket Location for $bucket"
# continue
# fi
# if [[ "None" == $BUCKET_LOCATION ]]; then
# BUCKET_LOCATION="us-east-1"
# fi
# if [[ "EU" == $BUCKET_LOCATION ]]; then
# BUCKET_LOCATION="eu-west-1"
# fi
# Verified with AWS support that if get-bucket-acl doesn't return a grant
# for All and get-bucket-policy-status returns IsPublic false or bad request
# (no policy) then the bucket can be considered not public - though
# individual objects may still be. If in addition put-public-access-block is
# used to set IgnorePublicAcls and RestrictPublicBuckets to true then that
# causes Amazon S3 to ignore all public ACLs on a bucket and any objects that
# it contains.
#
# 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!" "$BUCKET_LOCATION"
# else
# textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION"
# fi
# done
# }
# This check does not address legacy ACLs or policies that would give
# public access if not blocked at account or bucket level, instead it tries
# to reward the use of more broadly restrictive controls with quicker and less
# computational intensive checks.
#
# If we are assembling an inventory then maybe that is not what we want but
# for day to day usage that is probably desirable.
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 --output text)
#
# If public ACLs disabled at account level then look no further
#
ACCOUNT_PUBLIC_ACCESS_BLOCK=$($AWSCLI s3control get-public-access-block $PROFILE_OPT --region $REGION --account-id $ACCOUNT_NUM --output json 2>&1)
if [[ $(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to Get Public Access Block for $bucket"
return
fi
if [[ $(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | grep NoSuchPublicAccessBlockConfiguration) ]]; then
ACCOUNTIGNOREPUBLICACLS=""
ACCOUNTRESTRICTPUBLICBUCKETS=""
else
ACCOUNTIGNOREPUBLICACLS=$(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.IgnorePublicAcls')
ACCOUNTRESTRICTPUBLICBUCKETS=$(echo "$ACCOUNT_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.RestrictPublicBuckets')
fi
if [[ $ACCOUNTIGNOREPUBLICACLS == "true" && $ACCOUNTRESTRICTPUBLICBUCKETS == "true" ]]; then
textPass "All S3 public access blocked at account level"
return
fi
#
# Otherwise start to iterate bucket
#
ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' $PROFILE_OPT --output text 2>&1)
if [[ $(echo "$ALL_BUCKETS_LIST" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to List Buckets"
return
fi
if [[ "$ALL_BUCKETS_LIST" == "" ]]; then
textInfo "No buckets found"
return
fi
for bucket in $ALL_BUCKETS_LIST; do
# 3 Different problems, let's show only 1 finding all together
S3_FINDING_ALLUSERS_ACL="Ok"
S3_FINDING_AUTHUSERS_ACL="Ok"
S3_FINDING_POLICY="Ok"
# LOCATION
#
# LOCATION - requests referencing buckets created after March 20, 2019
# must be made to S3 endpoints in the same region as the bucket was
# created.
#
BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket $PROFILE_OPT --output text 2>&1)
if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to Get Bucket Location for $bucket"
@@ -70,111 +90,65 @@ extra73(){
BUCKET_LOCATION="eu-west-1"
fi
# EXPLICIT DENY
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
textInfo "$BUCKET_LOCATION: $bucket have an explicit Deny. Not possible to get ACL." "$bucket" "$BUCKET_LOCATION"
#
# If public ACLs disabled at bucket level then look no further
#
BUCKET_PUBLIC_ACCESS_BLOCK=$($AWSCLI s3api get-public-access-block $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output json 2>&1)
if [[ $(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to Get Public Access Block for $bucket"
continue
fi
if [[ $(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | grep NoSuchPublicAccessBlockConfiguration) ]]; then
BUCKETIGNOREPUBLICACLS=""
BUCKETRESTRICTPUBLICBUCKETS=""
else
# PUBLIC BLOCK
# https://docs.aws.amazon.com/cli/latest/reference/s3api/get-public-access-block.html
BUCKET_PUBLIC_BLOCK=$($AWSCLI s3api get-public-access-block --bucket $bucket $PROFILE_OPT --region $BUCKET_LOCATION 2>/dev/null)
BUCKET_PUBLIC_BLOCK_IGNOREPUBLICACL=$(echo $BUCKET_PUBLIC_BLOCK | jq .PublicAccessBlockConfiguration.BlockPublicAcls 2>/dev/null)
BUCKET_PUBLIC_BLOCK_BLOCKPUBLICPOLICY=$(echo $BUCKET_PUBLIC_BLOCK | jq .PublicAccessBlockConfiguration.BlockPublicPolicy 2>/dev/null)
BUCKET_PUBLIC_BLOCK_BLOCKPUBLICACLS=$(echo $BUCKET_PUBLIC_BLOCK | jq .PublicAccessBlockConfiguration.BlockPublicAcls 2>/dev/null)
BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET=$(echo $BUCKET_PUBLIC_BLOCK | jq .PublicAccessBlockConfiguration.RestrictPublicBuckets 2>/dev/null)
if [[ $BUCKET_PUBLIC_BLOCK_IGNOREPUBLICACL == "true" ]] && [[ $BUCKET_PUBLIC_BLOCK_BLOCKPUBLICPOLICY == "true" ]] && [[ $BUCKET_PUBLIC_BLOCK_BLOCKPUBLICACLS == "true" ]] && [[ $BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET == "true" ]]; then
textPass "$BUCKET_LOCATION: $bucket bucket is public blocked (public-access-block)" "$BUCKET_LOCATION"
else
## ACL
# 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)
## POLICY
BUCKET_POLICY_STATUS=$($AWSCLI s3api get-bucket-policy-status $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query PolicyStatus.IsPublic --output text 2>/dev/null)
if [[ $CHECK_BUCKET_ALLUSERS_ACL || $CHECK_BUCKET_AUTHUSERS_ACL || $CHECK_BUCKET_ALLUSERS_POLICY == "True" ]]; then
if [[ $CHECK_BUCKET_ALLUSERS_ACL || $CHECK_BUCKET_AUTHUSERS_ACL ]] && [[ $BUCKET_PUBLIC_BLOCK_IGNOREPUBLICACL != "true" ]];then
if [[ $CHECK_BUCKET_ALLUSERS_ACL ]];then
S3_FINDING_ALLUSERS_ACL="bucket ACL is open to the Internet (Everyone) with permissions: $CHECK_BUCKET_ALLUSERS_ACL_SINGLE_LINE"
fi
if [[ $CHECK_BUCKET_AUTHUSERS_ACL ]];then
S3_FINDING_AUTHUSERS_ACL="bucket ACL is open to Authenticated users (Any AWS user) with permissions: $CHECK_BUCKET_AUTHUSERS_ACL_SINGLE_LINE"
fi
fi
if [[ $BUCKET_PUBLIC_BLOCK_RESTRICPUBLICBUCKET != "true" ]] && [[ $BUCKET_POLICY_STATUS == "True" ]]; then
# Here comes the magic: Find Statement Allow, Principal * and No Condition
BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION=$($AWSCLI s3api get-bucket-policy $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket \
| jq '.Policy | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*") | select(has("Condition") | not)')
if [[ $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION ]]; then
# Let's do more magic and identify who can do what
BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS=$(echo $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION \
| jq '"[Principal: " + (.Principal|tostring) + " Action: " + (.Action|tostring) + "]"' )
S3_FINDING_POLICY="bucket policy allow perform actions: $BUCKET_POLICY_ALLOW_ALL_WITHOUT_CONDITION_DETAILS"
else
textPass "$BUCKET_LOCATION: $bucket bucket policy with conditions" "$BUCKET_LOCATION"
fi
fi
else
textPass "$BUCKET_LOCATION: $bucket bucket is not open" "$bucket" "$BUCKET_LOCATION"
fi
fi
BUCKETIGNOREPUBLICACLS=$(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.IgnorePublicAcls')
BUCKETRESTRICTPUBLICBUCKETS=$(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.RestrictPublicBuckets')
fi
if [[ $S3_FINDING_ALLUSERS_ACL != "Ok" ]] || [[ $S3_FINDING_AUTHUSERS_ACL != "Ok" ]] || [[ $S3_FINDING_POLICY != "Ok" ]] ; then
textFail "$BUCKET_LOCATION: (bucket: $bucket) ALLUSERS_ACL: $S3_FINDING_ALLUSERS_ACL | AUTHUSERS_ACL: $S3_FINDING_AUTHUSERS_ACL | BUCKET_POLICY: $S3_FINDING_POLICY" "$BUCKET_LOCATION"
if [[ $BUCKETIGNOREPUBLICACLS == "true" && $BUCKETRESTRICTPUBLICBUCKETS == "true" ]]; then
textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION"
continue
fi
#
# Check for public ACL grants
#
BUCKET_ACL=$($AWSCLI s3api get-bucket-acl $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --output json 2>&1)
if [[ $(echo "$BUCKET_ACL" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to Get Bucket Acl for $bucket"
continue
fi
ALLUSERS_ACL=$(echo "$BUCKET_ACL" | jq '.Grants[]|select(.Grantee.URI != null)|select(.Grantee.URI | endswith("/AllUsers"))')
if [[ $ALLUSERS_ACL != "" ]]; then
textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION"
continue
fi
AUTHENTICATEDUSERS_ACL=$(echo "$BUCKET_ACL" | jq '.Grants[]|select(.Grantee.URI != null)|select(.Grantee.URI | endswith("/AuthenticatedUsers"))')
if [[ $AUTHENTICATEDUSERS_ACL != "" ]]; then
textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION"
continue
fi
#
# Check for public access in policy
#
BUCKET_POLICY_STATUS=$($AWSCLI s3api get-bucket-policy-status $PROFILE_OPT --region $BUCKET_LOCATION --bucket $bucket --query PolicyStatus.IsPublic --output text 2>&1)
if [[ $(echo "$BUCKET_POLICY_STATUS" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to Get Bucket Policy Status for $bucket"
continue
fi
if [[ $(echo "$BUCKET_POLICY_STATUS" | grep NoSuchBucketPolicy) ]]; then
BUCKET_POLICY_STATUS="False"
fi
if [[ $BUCKET_POLICY_STATUS != "" && $BUCKET_POLICY_STATUS != "False" ]]; then
textFail "$BUCKET_LOCATION: $bucket bucket is Public!" "$BUCKET_LOCATION"
continue
fi
textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION"
done
}
# Then implementation below makes pararel checks but can reach AWS API limits
# and eventually doesn't work as expected
# extra73(){
# textTitle "$ID73" "$TITLE73" "NOT_SCORED" "EXTRA"
# textNotice "Looking for open S3 Buckets (ACLs and Policies) in all regions... "
# ALL_BUCKETS_LIST=$($AWSCLI s3api list-buckets --query 'Buckets[*].{Name:Name}' --profile $PROFILE --region $REGION --output text)
# for bucket in $ALL_BUCKETS_LIST; do
# extra73Thread $bucket &
# done
# wait
# }
# extra73Thread(){
# bucket=$1
# BUCKET_LOCATION=$($AWSCLI s3api get-bucket-location --bucket $bucket --profile $PROFILE --region $REGION --output text 2>&1)
# if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then
# textFail "Access Denied Trying to Get Bucket Location for $bucket"
# return
# fi
# 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 $PROFILE --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 $PROFILE --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 $PROFILE --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
# 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" "$BUCKET_LOCATION"
# fi
# if [[ $CHECK_BUCKET_ALLUSERS_POLICY ]];then
# 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" "$BUCKET_LOCATION"
# fi
# rm -fr $TEMP_POLICY_FILE
# }
+5 -5
View File
@@ -42,15 +42,15 @@ extra734(){
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
$AWSCLI s3api get-bucket-policy $PROFILE_OPT --bucket $bucket --output text --query Policy > $TEMP_SSE_POLICY_FILE 2>&1
if [[ $(grep AccessDenied $TEMP_SSE_POLICY_FILE) ]]; then
textFail "Access Denied Trying to Get Bucket Policy for $bucket"
rm -fr $TEMP_SSE_POLICY_FILE
rm -f $TEMP_SSE_POLICY_FILE
continue
fi
if [[ $(grep NoSuchBucketPolicy $TEMP_SSE_POLICY_FILE) ]]; then
textFail "No bucket policy for $bucket"
rm -fr $TEMP_SSE_POLICY_FILE
rm -f $TEMP_SSE_POLICY_FILE
continue
fi
@@ -58,14 +58,14 @@ extra734(){
CHECK_BUCKET_SSE_POLICY_PRESENT=$(cat $TEMP_SSE_POLICY_FILE | jq --arg arn "arn:aws:s3:::${bucket}/*" '.Statement[]|select(.Effect=="Deny" and ((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*") and .Action=="s3:PutObject" and .Resource==$arn and .Condition.StringEquals."s3:x-amz-server-side-encryption" != null)')
if [[ $CHECK_BUCKET_SSE_POLICY_PRESENT == "" ]]; then
textFail "Bucket $bucket does not enforce encryption!"
rm -fr $TEMP_SSE_POLICY_FILE
rm -f $TEMP_SSE_POLICY_FILE
continue
fi
CHECK_BUCKET_SSE_POLICY_VALUE=$(echo "$CHECK_BUCKET_SSE_POLICY_PRESENT" | jq -r '.Condition.StringNotEquals."s3:x-amz-server-side-encryption"')
textPass "Bucket $bucket has S3 bucket policy to enforce encryption with $CHECK_BUCKET_SSE_POLICY_VALUE"
rm -fr $TEMP_SSE_POLICY_FILE
rm -f $TEMP_SSE_POLICY_FILE
done
else
+4 -3
View File
@@ -43,6 +43,8 @@ extra741(){
rm -f $EC2_USERDATA_FILE
else
textFail "$regx: Potential secret found in $instance" "$regx"
# delete file to not leave trace, user must look at the instance User Data
rm -f $EC2_USERDATA_FILE
fi
else
mv $EC2_USERDATA_FILE $EC2_USERDATA_FILE.gz ; gunzip $EC2_USERDATA_FILE.gz
@@ -56,12 +58,11 @@ extra741(){
fi
else
textPass "$regx: No secrets found in $instance User Data or it is empty" "$regx"
rm -f $EC2_USERDATA_FILE
fi
fi
done
else
textInfo "$regx: No EC2 instances found" "$regx"
fi
done
# rm -rf $SECRETS_TEMP_FOLDER
rm -rf $SECRETS_TEMP_FOLDER
}
+2 -2
View File
@@ -45,12 +45,12 @@ extra742(){
rm -f $CFN_OUTPUTS_FILE
fi
else
textInfo "$regx: CloudFormation stack $stack has not Outputs" "$regx"
textInfo "$regx: CloudFormation stack $stack has no Outputs" "$regx"
fi
done
else
textInfo "$regx: No CloudFormation stacks found" "$regx"
fi
done
# rm -rf $SECRETS_TEMP_FOLDER
rm -rf $SECRETS_TEMP_FOLDER
}
+3 -2
View File
@@ -39,15 +39,16 @@ extra759(){
rm -f $LAMBDA_FUNCTION_VARIABLES_FILE
else
textFail "$regx: Potential secret found in Lambda function $lambdafunction variables" "$regx"
# delete file to not leave trace, user must look at the function
rm -f $LAMBDA_FUNCTION_VARIABLES_FILE
fi
else
textInfo "$regx: Lambda function $stalambdafunction has not variables" "$regx"
rm -f $LAMBDA_FUNCTION_VARIABLES_FILE
fi
done
else
textInfo "$regx: No Lambda functions found" "$regx"
fi
done
# rm -rf $SECRETS_TEMP_FOLDER
rm -rf $SECRETS_TEMP_FOLDER
}
+2 -3
View File
@@ -44,13 +44,12 @@ extra760(){
else
textFail "$regx: Potential secret found in Lambda function $lambdafunction code" "$regx"
# delete files to not leave trace, user must look at the function
# rm -fr $LAMBDA_FUNCTION_FOLDER
rm -fr $LAMBDA_FUNCTION_FOLDER
fi
done
else
textInfo "$regx: No Lambda functions found" "$regx"
rm -fr $LAMBDA_FUNCTION_FOLDER
fi
done
# rm -fr $SECRETS_TEMP_FOLDER
rm -fr $SECRETS_TEMP_FOLDER
}
+2
View File
@@ -26,10 +26,12 @@ extra764(){
$AWSCLI s3api get-bucket-policy $PROFILE_OPT --bucket $bucket --output text --query Policy > $TEMP_STP_POLICY_FILE 2>&1
if [[ $(grep AccessDenied $TEMP_STP_POLICY_FILE) ]]; then
textFail "Access Denied Trying to Get Bucket Policy for $bucket"
rm -f $TEMP_STP_POLICY_FILE
continue
fi
if [[ $(grep NoSuchBucketPolicy $TEMP_STP_POLICY_FILE) ]]; then
textFail "No bucket policy for $bucket"
rm -f $TEMP_STP_POLICY_FILE
continue
fi
+26 -14
View File
@@ -19,7 +19,6 @@
# --region <value> \
# --repository-name <value> \
# --image-scanning-configuration scanOnPush=true
CHECK_ID_extra765="7.65"
CHECK_TITLE_extra765="[extra765] Check if ECR image scan on push is enabled (Not Scored) (Not part of CIS benchmark)"
@@ -28,19 +27,32 @@ CHECK_TYPE_extra765="EXTRA"
CHECK_ALTERNATE_check765="extra765"
extra765(){
for regx in $REGIONS; do
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[*].[repositoryName]" --output text 2>&1)
if [[ $LIST_ECR_REPOS ]]; then
for region in $REGIONS; do
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[*].[repositoryName]" --output text 2>&1)
if [[ $(echo "$LIST_ECR_REPOS" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to describe ECR repositories"
continue
fi
if [[ ! -z "$LIST_ECR_REPOS" ]]; then
for repo in $LIST_ECR_REPOS; do
SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[?repositoryName==\`$repo\`].[imageScanningConfiguration.scanOnPush]" --output text|grep True)
if [[ $SCAN_ENABLED ]];then
textPass "$regx: ECR repository $repo has scan on push enabled" "$regx"
else
textFail "$regx: ECR repository $repo has scan on push disabled!" "$regx"
fi
done
SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[?repositoryName==\`$repo\`].[imageScanningConfiguration.scanOnPush]" --output text 2>&1)
case "$SCAN_ENABLED" in
"True")
textPass "$region: ECR repository $repo has scan on push enabled" "$region"
;;
"False")
textFail "$region: ECR repository $repo has scan on push disabled!" "$region"
;;
"None")
textInfo "$region: ECR repository $repo hs no scanOnPush status, newer awscli needed" "$region"
;;
"*")
textInfo "$region: ECR repository $repo has unknown scanOnPush status \"$SCAN_ENABLED\"" "$region"
;;
esac
done
else
textInfo "$regx: No ECR repositories found" "$regx"
fi
done
textInfo "$region: No ECR repositories found" "$region"
fi
done
}
-46
View File
@@ -1,46 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# Remediation:
#
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#configuring-instance-metadata-service
#
# aws ecr put-image-scanning-configuration \
# --region <value> \
# --repository-name <value> \
# --image-scanning-configuration scanOnPush=true
CHECK_ID_extra766="7.66"
CHECK_TITLE_extra766="[extra766] Check if ECR image scan on push is enabled (Not Scored) (Not part of CIS benchmark)"
CHECK_SCORED_extra766="NOT_SCORED"
CHECK_TYPE_extra766="EXTRA"
CHECK_ALTERNATE_check766="extra766"
extra766(){
for regx in $REGIONS; do
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[*].[repositoryName]" --output text 2>&1)
if [[ $LIST_ECR_REPOS ]]; then
for repo in $LIST_ECR_REPOS; do
SCAN_ENABLED=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query "repositories[?repositoryName==\`$repo\`].[imageScanningConfiguration.scanOnPush]" --output text|grep True)
if [[ $SCAN_ENABLED ]];then
textPass "$regx: ECR repository $repo has scan on push enabled" "$regx"
else
textFail "$regx: ECR repository $repo has scan on push disabled!" "$regx"
fi
done
else
textInfo "$regx: No ECR repositories found" "$regx"
fi
done
}
+11 -2
View File
@@ -10,6 +10,7 @@
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra769="7.69"
CHECK_TITLE_extra769="[extra769] Check if IAM Access Analyzer is enabled and its findings (Not Scored) (Not part of CIS benchmark)"
CHECK_SCORED_extra769="NOT_SCORED"
@@ -18,10 +19,18 @@ CHECK_ALTERNATE_check769="extra769"
extra769(){
for regx in $REGIONS; do
LIST_OF_ACCESS_ANALYZERS=$($AWSCLI accessanalyzer list-analyzers $PROFILE_OPT --region $regx --query analyzers[*].arn --output text)
LIST_OF_ACCESS_ANALYZERS=$($AWSCLI accessanalyzer list-analyzers $PROFILE_OPT --region $regx --query analyzers[*].arn --output text 2>&1)
if [[ $(echo "$LIST_OF_ACCESS_ANALYZERS" | grep -i "argument command: Invalid choice") ]]; then
textInfo "$regx: list-analyzers not supported, newer awscli needed" "$regx"
continue
fi
if [[ $(echo "$LIST_OF_ACCESS_ANALYZERS" | grep -i "AccessDeniedException") ]]; then
textFail "$regx: Access Denied trying to list-analyzers" "$regx"
continue
fi
if [[ $LIST_OF_ACCESS_ANALYZERS ]]; then
for accessAnalyzerArn in $LIST_OF_ACCESS_ANALYZERS;do
ANALYZER_ACTIVE_FINDINGS_COUNT=$($AWSCLI accessanalyzer list-findings $PROFILE_OPT --region $regx --analyzer-arn $accessAnalyzerArn --query 'findings[?status == `ACTIVE`].[id,status]' --output text | wc -l | tr -d ' ')
ANALYZER_ACTIVE_FINDINGS_COUNT=$($AWSCLI accessanalyzer list-findings $PROFILE_OPT --region $regx --analyzer-arn $accessAnalyzerArn --query 'findings[?status == `ACTIVE`].[id,status]' --output text | wc -l | tr -d ' ')
if [[ $ANALYZER_ACTIVE_FINDINGS_COUNT -eq 0 ]];then
textPass "$regx: IAM Access Analyzer $accessAnalyzerArn has no active findings" "$regx"
else
+34 -15
View File
@@ -10,6 +10,7 @@
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra77="7.7,7.07"
CHECK_TITLE_extra77="[extra77] Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark)"
CHECK_SCORED_extra77="NOT_SCORED"
@@ -20,20 +21,38 @@ CHECK_ALTERNATE_check707="extra77"
extra77(){
# "Ensure there are no ECR repositories set as Public (Not Scored) (Not part of CIS benchmark)"
textInfo "Looking for ECR repos in all regions... "
for regx in $REGIONS; do
LIST_OF_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $regx --query 'repositories[*].{Name:repositoryName}' --output text)
for ecr_repo in $LIST_OF_ECR_REPOS; do
TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-ecr-repo.policy.XXXXXXXXXX)
$AWSCLI ecr get-repository-policy --repository-name $ecr_repo $PROFILE_OPT --region $regx --output text > $TEMP_POLICY_FILE 2> /dev/null
# check if the policy has Principal as *
CHECK_ECR_REPO_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | 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_ECR_REPO_ALLUSERS_POLICY ]];then
textFail "$regx: $ecr_repo policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$regx"
else
textPass "$regx: $ecr_repo is not open" "$regx"
fi
done
rm -fr $TEMP_POLICY_FILE
for region in $REGIONS; do
LIST_ECR_REPOS=$($AWSCLI ecr describe-repositories $PROFILE_OPT --region $region --query "repositories[*].[repositoryName]" --output text 2>&1)
if [[ $(echo "$LIST_ECR_REPOS" | grep AccessDenied) ]]; then
textFail "Access Denied Trying to describe ECR repositories"
continue
fi
if [[ ! -z "$LIST_ECR_REPOS" ]]; then
for repo in $LIST_ECR_REPOS; do
TEMP_POLICY_FILE=$(mktemp -t prowler-${ACCOUNT_NUM}-ecr-repo.policy.XXXXXXXXXX)
$AWSCLI ecr get-repository-policy $PROFILE_OPT --region $region --repository-name $repo --query "policyText" --output text > $TEMP_POLICY_FILE 2>&1
if [[ $(grep AccessDenied $TEMP_POLICY_FILE) ]]; then
textFail "$region: $repo Access Denied for get-repository-policy"
rm -f $TEMP_POLICY_FILE
continue
fi
# https://docs.aws.amazon.com/AmazonECR/latest/userguide/repository-policies.html - "By default, only the repository owner has access to a repository."
if [[ $(grep RepositoryPolicyNotFoundException $TEMP_POLICY_FILE) ]]; then
textPass "$region: $repo is not open" "$region"
rm -f $TEMP_POLICY_FILE
continue
fi
# check if the policy has Principal as *
CHECK_ECR_REPO_ALLUSERS_POLICY=$(cat $TEMP_POLICY_FILE | jq '.Statement[]|select(.Effect=="Allow" and (((.Principal|type == "object") and .Principal.AWS == "*") or ((.Principal|type == "string") and .Principal == "*")))')
if [[ $CHECK_ECR_REPO_ALLUSERS_POLICY ]]; then
textFail "$region: $repo policy \"may\" allow Anonymous users to perform actions (Principal: \"*\")" "$region"
else
textPass "$region: $repo is not open" "$region"
fi
rm -f $TEMP_POLICY_FILE
done
else
textInfo "$region: No ECR repositories found" "$region"
fi
done
}
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra772="7.72"
CHECK_TITLE_extra772="[extra772] Check if elastic IPs are unused (Not Scored) (Not part of CIS benchmark)"
CHECK_SCORED_extra772="NOT_SCORED"
CHECK_TYPE_extra772="EXTRA"
CHECK_ALTERNATE_check772="extra772"
extra772(){
for region in $REGIONS; do
EIP_DUMP=$($AWSCLI ec2 describe-addresses ${PROFILE_OPT} --region $region)
EIP_LIST=$(echo $EIP_DUMP | jq -r '.Addresses[].AllocationId')
if [[ $EIP_LIST ]]; then
for eip in $EIP_LIST; do
ASSOCIATION_ID=$(echo $EIP_DUMP | jq -r --arg i "$eip" '.Addresses[]|select(.AllocationId==$i)|.AssociationId')
if [[ "$ASSOCIATION_ID" == "null" ]]; then
textFail "$region: EIP $eip is unused" $region
else
textPass "$region: EIP $eip is used" $region
fi
done
else
textInfo "$region: No Elastic IPs found" $region
fi
done
}
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_extra773="7.73"
CHECK_TITLE_extra773="[extra773] Check if CloudFront distributions are using WAF (Not Scored) (Not part of CIS benchmark)"
CHECK_SCORED_extra773="NOT_SCORED"
CHECK_TYPE_extra773="EXTRA"
CHECK_ALTERNATE_check773="extra773"
extra773(){
# "Check if CloudFront distributions have logging enabled (Not Scored) (Not part of CIS benchmark)"
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
WEB_ACL_ID=$($AWSCLI cloudfront get-distribution $PROFILE_OPT --id "$dist" --query 'Distribution.DistributionConfig.WebACLId' --output text)
if [[ $WEB_ACL_ID ]]; then
textPass "CloudFront distribution $dist is using AWS WAF web ACL $WEB_ACL_ID"
else
textFail "CloudFront distribution $dist is not using AWS WAF web ACL"
fi
done
else
textInfo "No CloudFront distributions found"
fi
}
-26
View File
@@ -1,26 +0,0 @@
import boto3
import sys
import json
import os
sts = boto3.client('sts')
aws_lambda = boto3.client('lambda', region_name='us-west-2')
aws_account_id = sys.argv[1]
if aws_account_id is None:
raise ValueError('Must provide an AWS account ID')
account_details_response = aws_lambda.invoke(
FunctionName='customers-api',
Payload=json.dumps({"path": "/invoke/getTenantDetails", "body": {"awsAccountId": sys.argv[1]}, "headers": {"Content-Type": "application/json"}}).encode('utf-8')
)
account_details = json.loads(json.loads(account_details_response['Payload'].read())['body'])
print('Got account details, assuming role')
temporary_creds = sts.assume_role(RoleArn=account_details['cross_account_role_arn'], ExternalId=account_details['external_id'], RoleSessionName='prowler')['Credentials']
os.environ['AWS_ACCESS_KEY_ID'] = temporary_creds['AccessKeyId']
os.environ['AWS_SECRET_ACCESS_KEY'] = temporary_creds['SecretAccessKey']
+1 -1
View File
@@ -15,7 +15,7 @@ GROUP_ID[7]='extras'
GROUP_NUMBER[7]='7.0'
GROUP_TITLE[7]='Extras - [extras] **********************************************'
GROUP_RUN_BY_DEFAULT[7]='Y' # run it when execute_all is called
GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra737,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra756,extra757,extra758,extra761,extra762,extra763,extra764,extra765,extra766,extra767,extra768,extra769,extra770,extra771'
GROUP_CHECKS[7]='extra71,extra72,extra73,extra74,extra75,extra76,extra77,extra78,extra79,extra710,extra711,extra712,extra713,extra714,extra715,extra716,extra717,extra718,extra719,extra720,extra721,extra722,extra723,extra724,extra725,extra726,extra727,extra728,extra729,extra730,extra731,extra732,extra733,extra734,extra735,extra736,extra737,extra738,extra739,extra740,extra741,extra742,extra743,extra744,extra745,extra746,extra747,extra748,extra749,extra750,extra751,extra752,extra753,extra754,extra755,extra756,extra757,extra758,extra761,extra762,extra763,extra764,extra765,extra767,extra768,extra769,extra770,extra771,extra772,extra773'
# Extras 759 and 760 (lambda variables and code secrets finder are not included)
# to run detect-secrets use `./prowler -g secrets`
+8 -1
View File
@@ -30,10 +30,12 @@
"dax:describeparameters",
"dax:describesubnetgroups",
"dax:describetable",
"dax:listtables",
"dax:listtables",
"devicefarm:list*",
"discovery:list*",
"dms:list*",
"ds:ListAuthorizedApplications",
"ds:DescribeRoles",
"dynamodb:describebackup",
"dynamodb:describeglobaltablesettings",
"dynamodb:describelimits",
@@ -50,6 +52,11 @@
"gamelift:list*",
"glacier:list*",
"importexport:listjobs",
"lambda:GetAccountSettings",
"lambda:GetFunctionConfiguration",
"lambda:GetLayerVersionPolicy",
"lambda:GetPolicy",
"lambda:List*",
"lex:getbotaliases",
"lex:getbotchannelassociations",
"lex:getbots",
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# both variables are mandatory to be set together
if [[ $ACCOUNT_TO_ASSUME ]]; then
if [[ -z $ROLE_TO_ASSUME ]]; then
echo "$OPTRED ERROR!$OPTNORMAL - Both Account ID (-A) and IAM Role to assume (-R) must be set"
exit 1
fi
# if not session duration set with -T, then will be 1h.
# In some cases you will need more than 1h.
if [[ -z $SESSION_DURATION_TO_ASSUME ]]; then
SESSION_DURATION_TO_ASSUME="3600"
fi
# temporary file where to store credentials
TEMP_STS_ASSUMED_FILE=$(mktemp -t prowler.sts_assumed-XXXXXX)
# assume role command
$AWSCLI $PROFILE_OPT sts assume-role --role-arn arn:aws:iam::$ACCOUNT_TO_ASSUME:role/$ROLE_TO_ASSUME \
--role-session-name ProwlerAssessmentSession \
--duration-seconds $SESSION_DURATION_TO_ASSUME > $TEMP_STS_ASSUMED_FILE
# if previous command fails exit with the given error from aws-cli
# this is likely to be due to session duration limit of 1h in case
# of assume role chaining:
# "The requested DurationSeconds exceeds the 1 hour session limit
# for roles assumed by role chaining."
# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html
if [[ $? != 0 ]];then
exit 1
fi
# The profile shouldn't be used for CLI
PROFILE=""
PROFILE_OPT=""
# set env variables with assumed role credentials
export AWS_ACCESS_KEY_ID=$(cat $TEMP_STS_ASSUMED_FILE | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(cat $TEMP_STS_ASSUMED_FILE | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(cat $TEMP_STS_ASSUMED_FILE | jq -r '.Credentials.SessionToken')
rm -fr $TEMP_STS_ASSUMED_FILE
fi
+10 -2
View File
@@ -13,6 +13,10 @@
# Output formatting functions
textPass(){
if [[ "$QUIET" == 1 ]]; then
return
fi
PASS_COUNTER=$((PASS_COUNTER+1))
if [[ "$MODE" == "csv" ]]; then
if [[ $2 ]]; then
@@ -55,6 +59,10 @@ textPass(){
}
textInfo(){
if [[ "$QUIET" == 1 ]]; then
return
fi
if [[ "$MODE" == "csv" ]]; then
if [[ $2 ]]; then
REPREGION=$2
@@ -68,7 +76,7 @@ textInfo(){
else
REPREGION=$REGION
fi
jq -c \
jq -M -c \
--arg PROFILE "$PROFILE" \
--arg ACCOUNT_NUM "$ACCOUNT_NUM" \
--arg TITLE_TEXT "$TITLE_TEXT" \
@@ -111,7 +119,7 @@ textFail(){
else
REPREGION=$REGION
fi
jq -c \
jq -M -c \
--arg PROFILE "$PROFILE" \
--arg ACCOUNT_NUM "$ACCOUNT_NUM" \
--arg TITLE_TEXT "$TITLE_TEXT" \
+37 -6
View File
@@ -40,6 +40,7 @@ FILTERREGION=""
MAXITEMS=100
MONOCHROME=0
MODE="text"
QUIET=0
SEP=','
KEEPCREDREPORT=0
EXITCODE=0
@@ -75,12 +76,19 @@ USAGE:
-V show version number & exit
-s show scoring report
-x specify external directory with custom checks (i.e. /my/own/checks, files must start by "check")
-q suppress info messages and passing test output
-A account id for the account where to assume a role, requires -R and -T
(i.e.: 123456789012)
-R role name to assume in the account, requires -A and -T
(i.e.: ProwlerRole)
-T session durantion given to that role credentials in seconds, default 1h (3600) recommended 12h, requires -R and -T
(i.e.: 43200)
-h this help
"
exit
}
while getopts ":hlLkp:r:c:g:f:m:M:E:enbVsx:" OPTION; do
while getopts ":hlLkqp:r:c:g:f:m:M:E:enbVsx:A:R:T:" OPTION; do
case $OPTION in
h )
usage
@@ -140,6 +148,18 @@ while getopts ":hlLkp:r:c:g:f:m:M:E:enbVsx:" OPTION; do
x )
EXTERNAL_CHECKS_PATH=$OPTARG
;;
q )
QUIET=1
;;
A )
ACCOUNT_TO_ASSUME=$OPTARG
;;
R )
ROLE_TO_ASSUME=$OPTARG
;;
T )
SESSION_DURATION_TO_ASSUME=$OPTARG
;;
: )
echo ""
echo "$OPTRED ERROR!$OPTNORMAL -$OPTARG requires an argument"
@@ -157,6 +177,9 @@ while getopts ":hlLkp:r:c:g:f:m:M:E:enbVsx:" OPTION; do
esac
done
# Clean up any temp files when prowler quits unexpectedly
trap "{ rm -f /tmp/prowler*.policy.*; }" EXIT
. $PROWLER_DIR/include/colors
. $PROWLER_DIR/include/os_detector
. $PROWLER_DIR/include/aws_profile_loader
@@ -170,6 +193,7 @@ done
. $PROWLER_DIR/include/python_detector
. $PROWLER_DIR/include/secrets_detector
. $PROWLER_DIR/include/check3x
. $PROWLER_DIR/include/assume_role
# Get a list of all available AWS Regions
REGIONS=$($AWSCLI ec2 describe-regions --query 'Regions[].RegionName' \
@@ -189,12 +213,12 @@ for checks in $(ls $PROWLER_DIR/checks/check*|grep -v check_sample); do
. "$checks"
done
# include checks if external folder is specified
if [[ $EXTERNAL_CHECKS_PATH ]]; then
# include checks if external folder is specified
if [[ $EXTERNAL_CHECKS_PATH ]]; then
for checks in $(ls $EXTERNAL_CHECKS_PATH/check*); do
. "$checks"
done
fi
fi
# Function to show the title of the check
# using this way instead of arrays to keep bash3 (osx) and bash4(linux) compatibility
@@ -287,7 +311,7 @@ execute_group_by_id() {
done
}
# Function to execute all checks in all groups except extras if -e is invoked
# 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
@@ -304,7 +328,7 @@ show_all_titles() {
MAIN_GROUPS=(1 2 3 4 7)
for i in "${MAIN_GROUPS[@]}"; do
show_group_title $i
# Display the title of the checks in groups 1,2,3,4 and 7
# Display the title of the checks in groups 1,2,3,4 and 7
# Any other group has checks in these groups
IFS=',' read -ra CHECKS <<< ${GROUP_CHECKS[$i]}
for j in ${CHECKS[@]}; do
@@ -415,6 +439,13 @@ if [[ $CHECK_ID ]];then
exit $EXITCODE
fi
if [[ $ACCOUNT_TO_ASSUME ]]; then
# unset env variables with assumed role credentials
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN
fi
execute_all
scoring
-19
View File
@@ -1,19 +0,0 @@
#!/bin/bash
mkdir ~/.aws
cat << AWS_CREDS > ~/.aws/credentials
[${ACCOUNT_ID}]
credential_source = EcsContainer
role_arn = ${ROLE_ARN}
external_id = ${EXTERNAL_ID}
AWS_CREDS
echo "Running prowler on ${ACCOUNT_ID}"
./prowler -p "${ACCOUNT_ID}" "${CHECKS}" -M json > output.json
echo "Results:"
cat output.json
echo "Uploading result to s3://${BUCKET}/prowler/${ACCOUNT_ID}/output.json"
aws s3api put-object --bucket "${BUCKET}" --key prowler/"${ACCOUNT_ID}"/output.json --body output.json