#!/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_extra73="7.3" CHECK_TITLE_extra73="[extra73] Ensure there are no S3 buckets open to Everyone or Any AWS user" CHECK_SCORED_extra73="NOT_SCORED" CHECK_TYPE_extra73="EXTRA" CHECK_SEVERITY_extra73="Critical" CHECK_ASFF_RESOURCE_TYPE_extra73="AwsS3Bucket" CHECK_ALTERNATE_extra703="extra73" CHECK_ALTERNATE_check73="extra73" CHECK_ALTERNATE_check703="extra73" CHECK_SERVICENAME_extra73="s3" CHECK_RISK_extra73='Even if you enable all possible bucket ACL options available in the Amazon S3 console the ACL alone does not allow everyone to download objects from your bucket. Depending on which option you select any user could perform some actions.' CHECK_REMEDIATION_extra73='You can enable block public access settings only for access points; buckets; and AWS accounts. Amazon S3 does not support block public access settings on a per-object basis. When you apply block public access settings to an account; the settings apply to all AWS Regions globally. The settings might not take effect in all Regions immediately or simultaneously; but they eventually propagate to all Regions.' CHECK_DOC_extra73='https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html' CHECK_CAF_EPIC_extra73='Data Protection' # 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. # # 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(){ # # 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 "$REGION: Access Denied getting PublicAccessBlock configuration for AWS account" "$REGION" "$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 "$REGION: All S3 public access blocked at account level" "$REGION" "$bucket" 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 "$REGION: Access Denied Trying to List Buckets" "$REGION" "$bucket" return fi if [[ "$ALL_BUCKETS_LIST" == "" ]]; then textInfo "$REGION: No buckets found" "$REGION" "$bucket" return fi for bucket in $ALL_BUCKETS_LIST; do # # 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 $PROFILE_OPT --region $REGION --bucket $bucket --output text 2>&1) if [[ $(echo "$BUCKET_LOCATION" | grep AccessDenied) ]]; then textFail "$REGION: Access Denied Trying to Get Bucket Location for $bucket" "$REGION" "$bucket" continue fi if [[ $BUCKET_LOCATION == "None" ]]; then BUCKET_LOCATION="us-east-1" fi if [[ $BUCKET_LOCATION == "EU" ]]; then BUCKET_LOCATION="eu-west-1" fi # # 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 "$BUCKET_LOCATION: Access Denied Trying to Get Public Access Block for $bucket" "$BUCKET_LOCATION" "$bucket" continue fi if [[ $(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | grep NoSuchPublicAccessBlockConfiguration) ]]; then BUCKETIGNOREPUBLICACLS="" BUCKETRESTRICTPUBLICBUCKETS="" else BUCKETIGNOREPUBLICACLS=$(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.IgnorePublicAcls') BUCKETRESTRICTPUBLICBUCKETS=$(echo "$BUCKET_PUBLIC_ACCESS_BLOCK" | jq -r '.PublicAccessBlockConfiguration.RestrictPublicBuckets') fi if [[ $BUCKETIGNOREPUBLICACLS == "true" && $BUCKETRESTRICTPUBLICBUCKETS == "true" ]]; then textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION" "$bucket" 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 "$BUCKET_LOCATION: Access Denied Trying to Get Bucket Acl for $bucket" "$BUCKET_LOCATION" "$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" "$bucket" 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" "$bucket" 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 "$BUCKET_LOCATION: Access Denied Trying to Get Bucket Policy Status for $bucket" "$BUCKET_LOCATION" "$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" "$bucket" continue fi textPass "$BUCKET_LOCATION: $bucket bucket is not Public" "$BUCKET_LOCATION" "$bucket" done }